REST API authentication: Resource Owner Password Credentials
The tracezilla REST API accepts OAuth 2.0 bearer token.
This document covers the Resource Owner Password Credentials grant
(grant_type=password): an integration exchanges a client ID, client secret,
username and password for a short-lived access token plus a refresh token.
Use it for server-to-server integrations, where a single client should be able to be used by multiple users. For a single long-lived token, a personal access token (the “API tokens” tab) is the simpler alternative.
1. Create the credentials
In the app, go to My account → API tokens → Client Credentials
(route my-account.api-tokens, ?tab=client-credentials) and fill in:
| Field | Meaning |
|---|---|
| Name | Label for the client (max 60 characters). Also used as the API user’s name. |
| Team (optional) | Creates a dedicated API user on that team. Only listed for teams where you are admin, owner or supporter. |
| Role | The dedicated API user’s role on the team. Required when a team is chosen. |
What gets created (ApiTokenController::createClientCredentials):
- With a team selected: a new user with a generated email,
{team_slug}.{5 random chars}.{s|u}@tracezilla.api(ufor umbrella teams,sotherwise), a random 36-character password, and membership of the team in the chosen role. A password client is created for that user. The client ID, client secret, username and password are shown once. - Without a team: a password client is created for your own user. Only the client ID and secret are shown; you authenticate with your own email and password.
Prefer the dedicated API user. It keeps integration access separate from a person’s login, its permissions are limited by the chosen role, and it does not break when someone changes their password.
The secret and the generated password are shown only once. Store them in the integration’s secret store straight away.
Revoking the client in the same tab deletes it. Tokens issued through it stop working.
2. Request an access token
1
2
3
4
5
6
7
8
9
10
POST https://{app-domain}/oauth/token
Content-Type: application/x-www-form-urlencoded
Accept: application/json
grant_type=password
&client_id={client_id}
&client_secret={client_secret}
&username={api_user_email}
&password={api_user_password}
&scope=all
curl example:
1
2
3
4
5
6
7
8
curl -X POST "https://{app-domain}/oauth/token" \
-H "Accept: application/json" \
-d grant_type=password \
-d client_id="$TZ_CLIENT_ID" \
-d client_secret="$TZ_CLIENT_SECRET" \
-d username="$TZ_USERNAME" \
-d password="$TZ_PASSWORD" \
-d scope=all
Successful response:
1
2
3
4
5
6
{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOi...",
"refresh_token": "def50200a1b2..."
}
username is the user’s email address. Wrong credentials return
400 invalid_grant, and an unknown or revoked client returns 401 invalid_client.
Scopes
The user will only ever have the permissions granted by the role assigned to the user.
| Scope | Description |
|---|---|
all |
All available CRUD actions (default when scope is omitted) |
3. Call the API
Send the access token as a bearer token. Every API route is prefixed with
/api/v1/{team_slug}:
1
2
3
curl "https://{app-domain}/api/v1/{team_slug}/lots" \
-H "Accept: application/json" \
-H "Authorization: Bearer $ACCESS_TOKEN"
The user behind the token must be a member of {team_slug}. What it can do is
limited by its role on that team.
Requests are rate-limited per user (throttle:100|rate_limit,1): 100 requests per
minute, unless the user’s rate_limit column sets a different value. A 429
response means wait and retry.
4. Refresh the token
Access tokens expire after 60 minutes. Use the refresh token to get a new pair without sending the password again:
1
2
3
4
5
6
7
8
9
POST https://{app-domain}/oauth/token
Content-Type: application/x-www-form-urlencoded
Accept: application/json
grant_type=refresh_token
&refresh_token={refresh_token}
&client_id={client_id}
&client_secret={client_secret}
&scope=all
The response has the same shape as in step 2. Replace both stored tokens. Refresh tokens are single-use, so the old one stops working once it has been exchanged.
Refresh tokens expire after 30 days. An integration idle for longer, or one that
gets 400 invalid_grant on refresh, should fall back to step 2.
Recommended client behaviour
- Cache the access token and reuse it until it is close to
expires_in. Do not request a new token for every API call. - Run one token holder per client and user. Parallel workers should share the cached token rather than each requesting their own (see the cleanup note below).
- Refresh proactively, about a minute before expiry, or on a
401from the API, then retry the request once. - Never log the client secret, password, access token or refresh token.