API Key Authentication
⚠️
This feature is still in BETA
API authentication via API keys is still under development and in beta. This feature requires API access enabled on your environment. Usage of custom connectors is a usage billed product unless you have an API package. Please note any usage of this whilst in BETA stage is free but following GR release, any usage not part of an agreed package will be billed at standard usage rates.
If you are an approved 3rd party connector, please reach out to Gladstone to discuss usage.
Token-based authentication for third-party web integrations. Replaces the cookie-based, browser-bound model. If you're building a web integration with Gladstone APIs, you must authenticate with an API key and bearer tokens — not cookies.
Prerequisites
Before you start, you can generate a key within the Operator portal:
- An API key in the format
gsk_{version}_{secret}— your integration's credential. Shown to you exactly once when issued. Store it securely; we cannot recover it. - The environment URL for the tenant you are integrating against.
- A registered Origin domain (browser-based integrations only — see Security notes).
Keep the API key in a server-side secret store. Do not embed it in browser JavaScript or any client-side code.
Integration types
There are two types of API key integration:
| Type | Description | Pricing |
|---|---|---|
| Marketplace Provider | A pre-built integration listed in the Gladstone marketplace. Available for any Gladstone customer to activate for their site. | Fixed cost |
| Custom API key | A bespoke integration scoped to a single Gladstone customer's site. Created and managed by the customer for their own use. | Chargeable per key & request |
Marketplace Providers are typically third-party software vendors with an existing onboarding agreement with Gladstone, these come pre-packaged with locked down security and included API requests. Custom API keys are issued for customer-specific integrations and are chargeable as part of your API usage package.
How authentication works
You exchange your API key for a short-lived JWT, then send that JWT as a Bearer token on
every subsequent API call.
[Your backend] -- API key --> POST /partner/token --> [JWT + refresh token]
[Your backend] -- Bearer JWT --> any Gladstone APIStep 1 — Get a token
POST https://{environment}/api/samlauthentication/partner/token
Always send:
| Header | Value |
|---|---|
X-Partner-Api-Key | your API key (gsk_{version}_{secret}) |
Origin | your registered domain (browser-based integrations only) |
Content-Type | application/json |
The request body decides the grant type:
a) Machine-to-machine token (no member context)
curl -X POST 'https://{environment}/api/samlauthentication/partner/token' \
-H 'X-Partner-Api-Key: gsk_sV_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
-H 'Origin: https://yourcompany.com' \
-H 'Content-Type: application/json' \
-d '{}'b) Member-scoped token (acting on behalf of a member)
curl -X POST 'https://{environment}/api/samlauthentication/partner/token' \
-H 'X-Partner-Api-Key: gsk_sV_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
-H 'Origin: https://yourcompany.com' \
-H 'Content-Type: application/json' \
-d '{ "email": "member@example.com", "password": "..." }'c) Refresh an expired token
curl -X POST 'https://{environment}/api/samlauthentication/partner/token' \
-H 'X-Partner-Api-Key: gsk_sV_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
-H 'Origin: https://yourcompany.com' \
-H 'Content-Type: application/json' \
-d '{ "refreshToken": "<your-refresh-token>" }'The previous refresh token is revoked immediately on every refresh — persist the new one returned.
Member passwords are encrypted in transit. Please encrypt the password in the following way:
Password transit encryption
Member passwords must be AES-256-CBC encrypted before being included in the request body. The server decrypts the password server-side before verifying it against the member's stored credentials. Sending a plaintext password always returns 401 Unauthorized.
Encryption algorithm
| Step | Detail |
|---|---|
| Key | SHA-256( UTF-8(hostname) ) — 32 bytes. hostname is the bare hostname of the URL you are POSTing to (e.g. host-a.gladstonego.cloud). |
| IV | 16 cryptographically random bytes, generated fresh for every request. |
| Cipher | AES-256-CBC, PKCS7 padding. |
| Wire value | Base64( IV_bytes + ciphertext_bytes ) — the 16 IV bytes are prepended to the ciphertext before Base64 encoding. |
Hostname binding — important. The encryption key is derived from the exact hostname you are calling. A password encrypted for
host-a.example.comwill always fail onhost-b.example.com, even if both resolve to the same site. If your integration calls more than one environment URL, you must encrypt the password separately for each.
JavaScript (example)
async function encryptPassword(plaintext, hostname) {
const enc = new TextEncoder();
// Derive 32-byte AES key from the request hostname
const keyBytes = await crypto.subtle.digest('SHA-256', enc.encode(hostname));
const key = await crypto.subtle.importKey(
'raw', keyBytes, { name: 'AES-CBC' }, false, ['encrypt']
);
// Random 16-byte IV — must be unique per call
const iv = crypto.getRandomValues(new Uint8Array(16));
const ciphertext = await crypto.subtle.encrypt(
{ name: 'AES-CBC', iv },
key,
enc.encode(plaintext)
);
// Prepend IV to ciphertext and Base64-encode
const combined = new Uint8Array(16 + ciphertext.byteLength);
combined.set(iv, 0);
combined.set(new Uint8Array(ciphertext), 16);
return btoa(String.fromCharCode(...combined));
}
// Usage — hostname must match the URL you POST to
const hostname = 'host-a.gladstonego.cloud';
const encryptedPassword = await encryptPassword('memberP@ssword', hostname);
// Pass encryptedPassword as "password" in the request bodyResponse
All three grants return the same JSON shape (illustrative — expiresIn is environment-dependent):
{
"accessToken": "eyJhbGciOi...",
"refreshToken": "f3a1c4b2-...",
"tokenType": "Bearer",
"expiresIn": 3600
}accessToken— JWT. Send asAuthorization: Bearer <access-token>on subsequent API calls.refreshToken— long-lived. Exchange via grant (c) before the access token expires.tokenType— always"Bearer".expiresIn— seconds untilaccessTokenexpires. Treat this as the source of truth — the lifetime is configured per environment, so don't hardcode an assumed duration.
Step 2 — Call a Gladstone API
After authentication, every API request must include:
curl 'https://{environment}/api/bookings/availability/...' \
-H 'Authorization: Bearer <accessToken from step 1>'
-H 'X-Use-SSO: true'The bearer token carries your integration identity, tenant context, and the features available to your account. It is accepted across the Gladstone API, giving you access to the API endpoints relevant to your use case — for example bookings, member profile, and activities.
Remember to pass the "X-Use-SSO" as a header to allow it to authenticate without user interaction.
What your token can reach
You do not request scopes. The token request has no scope field, and every token issued from
an API key carries the same partner access. (The cloud api scope belongs to the legacy
OAuth2 flow only.) What you can call is decided by three things:
- The endpoint must be open to partners. Every endpoint in this portal's
API reference is. An endpoint that is not documented here refuses partner
tokens with
403 Forbidden. - Your token must carry one of the endpoint's products and one of its features. Each
endpoint page lists them at the top, for example Products: Operator (
FOH), Consumer (CONSUMER) (any one of these products, not all). Any one listed value is enough; "not restricted" means that check does not apply. Which products and features your token carries depends on the token:- an API-only token carries the ones Gladstone configured for your integration;
- a member-scoped token carries the ones the Gladstone customer you are working with is
licensed for.
A token without a match gets
403 Forbidden.
- Your integration must be allowed the kind of call. Gladstone sets read, write and delete
permission for your integration: read covers
GET, write coversPOST,PUTandPATCH, and delete coversDELETE. A call your integration is not allowed returns403 Forbiddenwith a message such asPartner token lacks can_write capability.
Products, features and permissions are set on your integration, not on an individual key, so every key you hold has the same access. They are written into the token when it is issued: after Gladstone changes them, request a new token to pick the change up. To get access to an endpoint you cannot reach, contact Gladstone with the endpoint and the products and features its page lists.
Rate limits
Every API key has per-minute and per-hour request limits configured by Gladstone for your integration. If you exceed either limit you'll receive:
HTTP 429 Too Many Requests
Retry-After: <seconds>Wait the number of seconds indicated in Retry-After before retrying. Your integration
must handle 429 responses.
Deep Link
To deep link into Go with a token, please navigate to Go using the following URL
// Authenticated member, standard flow
https://[portal-domain]?SsoConfirmationToken=<token>
// Authenticated member, My Account
https://[portal-domain]/account?SsoConfirmationToken=<token>Errors
| Status | Means | Likely cause |
|---|---|---|
| 400 | Bad request | Malformed JSON or wrong grant-type body |
| 401 | Unauthorized | Missing, invalid, expired or revoked token or API key; unregistered Origin (browser); bad credentials; expired refresh token; password not encrypted |
| 403 | Forbidden | Valid token, but no access to this endpoint: the endpoint is not open to partners, your token has none of its products or features, or your integration lacks read, write or delete permission. See What your token can reach |
| 429 | Rate-limited | Slow down; honour Retry-After |
| 500 | Server error | Transient — retry with backoff; contact us if it persists |
Security notes
- Never put the API key in browser JavaScript or any client-side code. Anyone viewing page source can copy it. Store it server-side only.
- Browser integrations must use a registered domain. Your domain has to be registered
with Gladstone at setup time, and every request must include an
Originheader matching it. Requests from unregistered domains are rejected with401 Unauthorized. - The Origin check only blocks another website's browser code from reusing the key — it does not stop someone calling our API directly from a script, curl, or their own server. A key visible in page source is effectively public.
- API keys can be revoked or rotated at any time. Your integration must handle
401auth failures gracefully. - Audit logging. All authentication activity is logged against your API key — successful logins, failed attempts, token refreshes, and rate-limited requests. Gladstone administrators can review this log to support incident investigation and usage monitoring.
- If a key is exposed, contact us to revoke and reissue — we can do this without changing your partner record.
Worked example — back-end proxy pattern
[Browser] --login form--> [Your backend]
|
|--POST /partner/token (key + member creds)
| X-Partner-Api-Key, Origin
v
[Gladstone] --> member-scoped JWT
|
v
[Browser] <-- JWT
[Browser] --bookings/availability JWT--> [Gladstone]This keeps the API key off the client entirely; the browser only ever carries a short-lived member JWT.
Migration from cookie-based authentication
If your existing integration relies on cookies, the table below summarises the required changes:
| Currently | Required change |
|---|---|
| Cookie-based session | Token-based auth (bearer JWT) |
| Implicit login state | Explicit token storage and refresh |
| Domain-dependent | Works cross-domain (no Gladstone-domain dependency) |
Important constraints
- API keys can be revoked at any time → integration must handle
401failures. - Access tokens have a finite lifetime (returned as
expiresIn, in seconds) → refresh logic is required. - API keys must not be exposed in browser or client code.
- Browser integrations must use approved, registered origins.
- Rate limits are enforced per API key → implement
429+Retry-Afterhandling.
Quick-start checklist
To integrate with Gladstone APIs:
- Obtain an API key from the Operator portal.
- Register your domain (browser-based integrations only).
- Call
POST /partner/tokento get a bearer token — the body determines the grant type (empty for API-only,email+passwordfor member auth,refreshTokenfor refresh). - Send
Authorization: Bearer <access-token>on every subsequent request. - Implement token refresh (on
401) and rate-limit handling (on429). - Store and use tokens via your backend — never expose the API key in client code.