Authentication & Access

OAuth 2.0 client credentials, credential-bound tenancy, and the auth failures you can hit.

The WestIQ Platform API uses the OAuth 2.0 client credentials grant for server-to-server access. Each approved partner receives a client_id and client_secret during onboarding, together with the token endpoint URL for their integration.

Treat your client_secret like a password. Store it in a secrets manager, never in source control, and rotate it if you suspect exposure.

1. Get a token

Exchange your client credentials for a short-lived bearer access token at your token endpoint (a Cognito hosted domain; the exact URL is in your onboarding pack):

$curl -X POST "https://<your-westiq-auth-domain>/oauth2/token" \
> -u "$WESTIQ_CLIENT_ID:$WESTIQ_CLIENT_SECRET" \
> -H "Content-Type: application/x-www-form-urlencoded" \
> -d "grant_type=client_credentials&scope=westiq/search"
1{
2 "access_token": "eyJraWQiOi…",
3 "expires_in": 3600,
4 "token_type": "Bearer"
5}

Send it on every request as Authorization: Bearer <token>, and cache it until it expires rather than requesting one per call.

Only access tokens are accepted. Sending an ID token — or any token whose token_use is not access — is rejected with 401 auth.invalid_token.

2. Your credential IS your airline

Every partner credential is bound to its airline on the WestIQ side: the token’s client_id is resolved through a server-side mapping to your airline_id. You never send an airline identifier — not as a header, not as a body field, not as a token claim. There is nothing to configure and nothing to get wrong.

This is a deliberate isolation property, not a convenience:

  • A request can only ever act as the airline its credential is bound to. Tenancy cannot be forged, because the request carries no tenancy input.
  • A credential with no airline mapping is rejected with 403 auth.unknown_client — never resolved to a default tenant.
  • Your searches see your airline’s catalog namespace plus the shared global namespace, and nothing else.

You can observe the resolution in every search response: parsed.filters.airlineId is the airline your credential resolved to.

3. One scope: westiq/search

Every /v1 endpoint — search and events alike — requires the westiq/search scope on the access token. Request it in the token call (as in the example above). A token without it is rejected with 403 auth.insufficient_scope.

4. Auth failures you will see

All auth failures return the standard error envelope:

HTTPCodeCause
401auth.invalid_tokenMissing, malformed, expired, bad-signature, or wrong-issuer token — or an ID token instead of an access token
403auth.insufficient_scopeToken valid, but the westiq/search scope is absent
403auth.unknown_clientToken valid, but the client has no airline mapping

Unmatched routes also return 401, by design (the API denies by default). If a request 401s unexpectedly, check the path before debugging your token.

Rate limits

The platform edge applies a per-IP rate limit of 2000 requests per 5-minute window. Requests over the limit are blocked at the edge with a plain 403 — without the API’s error envelope — and the block clears on its own once your request rate drops. If you see bare 403s under load, back off and retry with jitter.