Authentication
Tilt uses OAuth 2.0 client_credentials for M2M (machine-to-machine) authentication. There are no user logins, cookies, or redirect flows — just a client_id + client_secret exchanged for a short-lived JWT access token.
Token endpoint
| Environment | Token URL |
|---|---|
dev | https://tilt-m2m-dev.auth.us-east-1.amazoncognito.com/oauth2/token |
prod | https://tilt-m2m-prod.auth.us-east-1.amazoncognito.com/oauth2/token |
Requesting an access token
curl -s -X POST \ https://tilt-m2m-dev.auth.us-east-1.amazoncognito.com/oauth2/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=${CLIENT_ID}" \ -d "client_secret=${CLIENT_SECRET}" \ -d "scope=tilt/payments:create tilt/payments:read tilt/locations:read"import qs from "node:querystring";
async function getToken({ clientId, clientSecret, scopes, tokenUrl }) { const res = await fetch(tokenUrl, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: qs.stringify({ grant_type: "client_credentials", client_id: clientId, client_secret: clientSecret, scope: scopes.join(" "), }), }); if (!res.ok) throw new Error(`Token exchange failed: ${res.status}`); return res.json(); // { access_token, expires_in, token_type }}import httpx
def get_token(client_id: str, client_secret: str, scopes: list[str], token_url: str) -> str: resp = httpx.post( token_url, data={ "grant_type": "client_credentials", "client_id": client_id, "client_secret": client_secret, "scope": " ".join(scopes), }, ) resp.raise_for_status() return resp.json()["access_token"]Response
{ "access_token": "eyJraWQiOiJ…", "expires_in": 3600, "token_type": "Bearer"}Using the token
Pass the access token as a Bearer header on every API request:
Authorization: Bearer eyJraWQiOiJ…Tokens are valid for 3600 seconds (1 hour). Cache the token locally and re-mint it within 60 seconds of expiry. Do not mint a new token on every request — each call has a small latency cost and Cognito has per-client rate limits.
Scopes
Scopes are requested at token-mint time and constrain what the token can do. Request only the scopes your integration needs.
| Scope | Description |
|---|---|
tilt/payments:read | Read payment records for orders in your partner scope |
tilt/payments:create | Create orders and initiate payments |
tilt/payments:refund | Issue refunds |
locations:read | List locations in your partner scope |
locations:write | Create/update locations (requires super_admin grant) |
webhooks:subscribe | Register webhook endpoints (requires super_admin grant) |
Scopes available to your client are set at creation time in the admin portal. A token request asking for a scope not granted to the client will be rejected by the token endpoint.
Token lifetime and caching
issued_at +0s → valid, use freely +3500s → refresh-window: mint a new token in background +3600s → token expires; all API calls return 401The authorizer caches valid tokens for up to 5 minutes. Revoking a client in the admin portal takes effect within 5 minutes (cache TTL) plus the token’s remaining lifetime.
Secret rotation
To rotate a client secret without downtime:
- Click Rotate secret in the admin portal → Integrations tab → API clients
- The new secret is shown once — copy it immediately
- The old secret remains valid for 24 hours (grace window), giving you time to deploy the new secret
- After 24 hours the old secret stops working
Revoking a client
Revoking a client immediately prevents new tokens from being minted. Existing tokens remain valid until they expire (up to 1 hour) because the API’s authorizer cache does not pre-invalidate issued JWTs. If you need immediate revocation, contact your Tilt account manager to hard-invalidate the token.
Error responses
| HTTP | error | Cause |
|---|---|---|
400 | invalid_request | Missing grant_type, client_id, or client_secret |
400 | invalid_scope | Requested scope not granted to this client |
401 | invalid_client | Wrong client_id / client_secret, or client revoked |
401 | — | API call with an expired or tampered token |
403 | insufficient_scope | Token lacks the required scope for the endpoint |
All API error bodies follow the standard Tilt error envelope — see the Error catalogue.