Authentication
Every route under /api/integrations/v1 takes a bearer credential in the Authorization header: the key itself, or an access token issued for it. Both pass the same checks on every call: the key is not revoked or expired, the call comes from an allowed address, the key holds the route’s scope, and your company still has the Integration API.
Keys work only on /api/integrations/v1. Every other part of the platform refuses them.
Direct key
Section titled “Direct key”Send the key on every call. This is the shortest way for any tool that can send a fixed header (a BPM tool, an ETL job, a script).
curl "https://app.quatlasglobal.com/api/integrations/v1/shipment-requests/<request-id>" \ -H "Authorization: Bearer $QUATLAS_KEY"OAuth 2.0 client credentials
Section titled “OAuth 2.0 client credentials”Where your security policy does not allow a static key on every call, exchange the key for a short-lived access token at the token endpoint (RFC 6749, section 4.4) and send the token instead.
| Token endpoint | POST https://app.quatlasglobal.com/api/integrations/v1/oauth/token |
| Body | application/x-www-form-urlencoded, grant_type=client_credentials |
| Client id | The key’s prefix (qk_ or qk_test_ and the next 12 characters) |
| Client secret | The key itself |
| Token lifetime | 900 seconds (15 minutes) |
Authenticate the client with HTTP Basic (client_secret_basic) or with the client_id and client_secret form fields (client_secret_post), never both in one request.
curl "https://app.quatlasglobal.com/api/integrations/v1/oauth/token" \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d grant_type=client_credentialscurl "https://app.quatlasglobal.com/api/integrations/v1/oauth/token" \ -d grant_type=client_credentials \ --data-urlencode "client_id=$CLIENT_ID" \ --data-urlencode "client_secret=$CLIENT_SECRET"{ "access_token": "eyJ...", "token_type": "Bearer", "expires_in": 900, "scope": "requests:read requests:write"}Then call the API with Authorization: Bearer <access_token>. Get a new token before expires_in runs out; the answer is never cached (Cache-Control: no-store).
- Scopes. Send an optional
scopefield (space-separated) to ask for fewer scopes than the key holds. Without it the token gets all of the key’s scopes. Asking for one the key does not hold answersinvalid_scope. - Revocation. A token is valid only while its key is: the key is checked again on every call, so revoking the key refuses its tokens at once.
- Limits. The token endpoint allows 20 requests a minute per IP address.
- Errors follow RFC 6749, section 5.2:
| Status | error |
When |
|---|---|---|
| 400 | invalid_request |
Not form-encoded, a field sent twice, both Basic and form credentials, or client_id differs from the Basic user. |
| 400 | unsupported_grant_type |
grant_type is not client_credentials. |
| 400 | invalid_scope |
A requested scope is not granted to the key. |
| 401 | invalid_client |
The client could not be authenticated. The reason is never given. |
OAuth only
Section titled “OAuth only”A key can be set to OAuth only when its integration is created. The key itself is then refused on every route but the token endpoint, so the key never travels on ordinary calls, and every call carries a token that dies in 15 minutes.
Refusals
Section titled “Refusals”| Status | code |
Meaning |
|---|---|---|
| 401 | Integration.Unauthorized |
No valid key or token: wrong, revoked, expired, from outside the IP allowlist, an OAuth-only key sent directly, or an expired token. The answer is the same for every reason; your Owner or Admin sees the call in the call log. |
| 403 | Integration.Forbidden |
The key is valid but lacks the route’s scope. |
| 403 | Integration.AccessDenied |
The key may not be used right now. Ask your Owner or Admin. |
| 403 | Company.Suspended |
Your company’s account is suspended. |
Rotation
Section titled “Rotation”Rotate a key before it expires, or whenever it may have been exposed. Your Owner or an Admin chooses Rotate on the key in Settings > Integrations (with two-step verification):
- A new key is issued for the same integration, with the same scopes, IP allowlist and OAuth setting, and shown once.
- The old key keeps working for 7 days, so you can put the new one into your systems without downtime. Revoking the old key ends that grace at once.
- An integration holds at most 2 active keys at a time.
Expiry
Section titled “Expiry”Every key expires, at most 12 months after it is issued (12 months by default). Your company’s Owner and Admins are reminded in the app and by e-mail 30 days, 7 days and 1 day before. After expiry the key answers 401. Rotate before then.
IP allowlist
Section titled “IP allowlist”A key can be limited to the addresses your calls come from: up to 20 entries, each an IPv4 or IPv6 address or a CIDR block such as 203.0.113.0/24. An empty list allows any source. A call from any other address answers 401. If your systems have fixed outbound addresses, we recommend filling it in.
Keeping keys safe
Section titled “Keeping keys safe”- Keep keys in a secret store, never in code, e-mail or chat.
- Grant each integration only the scopes it needs, and use one integration per system.
- Use a test key everywhere except production.
- Revoke at once if a key may have leaked: revocation takes effect immediately, for its tokens too.
