Authentication OAuth 2.0

Every WorkAxle API call must carry a valid access token in the Authorization header. Tokens are issued by Auth0 using OAuth 2.0's client-credentials flow.

The model in one paragraph

WorkAxle delegates authentication to Auth0. You exchange a client ID and client secret for a short-lived bearer token, then send that token on every request. There's a separate Auth0 tenant for staging and production. The App schema, Integration schema, and REST API all share a single audience per environment — access to each GraphQL surface is controlled by scopes on your Auth0 client.

Getting credentials

Credentials are issued per environment, per integration:

  • Staging credentials — request through your WorkAxle account team or partnership manager. There is no self-serve sandbox today; integration ops issues the Auth0 client tied to a staging tenant.
  • Production credentials — issued by the WorkAxle account team after a security review. Production tokens are tenant-scoped and can only access the customer organization they're tied to.

You'll receive a client ID, a client secret, and the audience value to request.

The token endpoint

Request
curl -X POST 'https://workaxle-staging.us.auth0.com/oauth/token' \
  -H 'Content-Type: application/json' \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "audience": "https://api.staging.workaxle.com/v1"
  }'

The staging tenant is workaxle-staging.us.auth0.com and the staging audience is https://api.staging.workaxle.com/v1. The /v1 is part of the Auth0 audience identifier — it is not an API route and does not make this a REST-only token: REST endpoints live under /api/v1/ and GraphQL under /v2/..., but both are called with this same audience token. Production uses a separate Auth0 tenant and audience (https://api.app.workaxle.com/v1), provided by your account team when production credentials are issued.

Response
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read:employees write:shifts ..."
}

expires_in is in seconds. After it elapses the token is rejected with 401.

Using the token

curl -X POST 'https://api.staging.workaxle.com/v2/graphql' \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"query": "{ me { id } }"}'

The same header format works on all three surfaces (App GraphQL, Integration GraphQL, REST).

Schema scope: app vs. integration

The App schema and the Integration schema share a single Auth0 audience (https://api.staging.workaxle.com/v1 on staging). Access to each surface is controlled by scopes on your Auth0 client: the App schema (/v2/graphql) requires access:app, and the Integration schema (/v2/integration/graphql) requires access:integration.

SurfaceTypical useFor
App schemaUser-facing flowsApps acting on behalf of a logged-in user
Integration schemaBulk import/export, M2MConnectors, sync jobs, partner backends

A single client may be granted both scopes (access to both surfaces) or just one — your account team scopes the client when it's issued. If a client calls a schema it lacks the scope for, the request returns 403 Forbidden even with a valid token.

App schema (/v2/graphql) callers: machine-to-machine tokens are not tenant-scoped, so App schema requests also require a company-id header identifying the target company. The Integration schema (/v2/integration/graphql) maps the company server-side and does not need this header.

Token lifecycle

  • Validity — typically one hour (expires_in: 3600). Always honor the response value rather than hardcoding.
  • Refresh — client-credentials tokens don't use refresh tokens. Request a new one when the current one is close to expiring.
  • Caching — cache the token in memory for the duration of expires_in. Don't re-mint per request — Auth0 rate-limits aggressively.
  • Revocation — secrets can be rotated by the issuing team. A revoked secret immediately invalidates future token requests; outstanding tokens remain valid until expiry.

Errors

StatusBody shapeMeaning
401{ "errors": [{ "message": "..." }] } (GraphQL) or { "error": "..." } (REST)Missing, expired, malformed, or wrong-audience token
403Same shapesToken valid but the client lacks the scope
429Retry-After headerRate limit hit — back off and retry

Security checklist

  • Store secrets in a secret manager or environment variables — never in source control.
  • Rotate secrets periodically and immediately on suspected compromise.
  • Use the narrowest scope set that gets the job done (request only access:app or access:integration as needed).
  • Don't share a single client across multiple integrations — separate clients make audit and rotation simpler.
  • TLS is mandatory; HTTP requests are rejected.