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
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.
{
"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.
| Surface | Typical use | For |
|---|---|---|
| App schema | User-facing flows | Apps acting on behalf of a logged-in user |
| Integration schema | Bulk import/export, M2M | Connectors, 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.
/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
| Status | Body shape | Meaning |
|---|---|---|
401 | { "errors": [{ "message": "..." }] } (GraphQL) or { "error": "..." } (REST) | Missing, expired, malformed, or wrong-audience token |
403 | Same shapes | Token valid but the client lacks the scope |
429 | Retry-After header | Rate 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:apporaccess:integrationas needed). - Don't share a single client across multiple integrations — separate clients make audit and rotation simpler.
- TLS is mandatory; HTTP requests are rejected.