Authentication
Every request to the Refersion API is authenticated with a Bearer token in the
Authorization header. For programmatic access you obtain that token by exchanging your Refersion
account's OAuth client credentials (a Client ID and Client Secret) for an access token, then
send the token on each API call. You choose between a one-hour and a 90-day token when you request
it — see Step 2.
This guide walks through the full flow: generate credentials, exchange them for an access token, and call the API. The endpoints themselves are listed in the sidebar, grouped by resource.
Who authenticates
OAuth client credentials belong to a Refersion account, not to an individual person. A token issued from those credentials acts on behalf of the account that generated them and can reach only that account's data — a token never crosses account boundaries.
A client_credentials token is a machine credential: it identifies your application rather than
a person signing in.
Step 1 — Get credentials
Generate an OAuth client from your account under Settings → Integrations → Refersion API Keys. In the OAuth section, choose Generate OAuth Client. Refersion issues a Client ID and a Client Secret — the two values you exchange for an access token in Step 2.
That same section lists every non-revoked OAuth client on your account with its currently live tokens, and is where you revoke a client you no longer use.
The Client Secret is shown only once, at the moment the client is created. It cannot be retrieved later. Store it somewhere secure. If you lose it, generate a new OAuth client and update your integration with the new credentials.
Revoking an OAuth client invalidates the client and every access token issued to it, straight away. Any integration still using those credentials stops working and no further token can be issued for them. Revoking the client is the only way to kill a leaked token outright — individual tokens cannot be revoked on their own.
Step 2 — Exchange credentials for a token
Send a POST request to the /oauth/token endpoint. The endpoint accepts either form-encoded
fields or a JSON body containing your client_id and your client_secret. This example uses
form-encoded fields:
curl -X POST https://auth.refersion.com/oauth/token \
-d client_id=<client_id> \
-d client_secret=<client_secret> \
-d token_type=long_lived
A successful response returns the access token and its lifetime:
{
"token_type": "Bearer",
"expires_in": 7776000,
"access_token": "<access_token>"
}
Choosing a token lifetime
The optional token_type request parameter selects how long the issued token lives:
token_type | Lifetime | expires_in |
|---|---|---|
short_lived | 1 hour | 3600 |
long_lived | 90 days | 7776000 |
Omitting token_type is the same as sending short_lived, so integrations built before this
parameter existed are unaffected and keep receiving one-hour tokens.
Any other value — including an empty one — is rejected with an invalid_request error and no token
is issued.
token_type means two different things hereThe token_type you send in the request selects the lifetime (short_lived / long_lived).
The token_type in the response is the OAuth 2.0 token class and is always "Bearer". They are
unrelated fields that happen to share a name.
Read expires_in rather than assuming 3600. It is the authoritative lifetime in seconds for the
token you were just issued, and it changes with token_type.
Request a token once and reuse it for the full expires_in window. Do not request a new token for each API call.
POST /oauth/token is rate limited to 60 requests per minute per Client ID and 600 requests per minute per IP address. Exceeding either limit returns 429 Too Many Requests with a Retry-After header giving the number of seconds to wait before retrying.
A client can hold one live token of each type at a time. Requesting a long_lived token immediately
revokes that client's previous long_lived token and leaves its short_lived token working, and
vice versa. Roll credentials by requesting the replacement and switching to it — the old token of
that type stops working the moment the new one is issued.
A 90-day token is convenient, but it also widens the window during which a leaked token stays usable. If one is exposed, revoke the whole client (Step 1) rather than waiting for expiry.
No refresh token is issued for the client_credentials grant — when a token expires, request a new
one by repeating this step.
Step 3 — Call the API
Send the access token in the Authorization header, prefixed with Bearer, on every request to
https://api.refersion.com/v2026-08:
curl https://api.refersion.com/v2026-08/offers \
-H "Authorization: Bearer <access_token>"
Rate limits
Read and write requests are limited per account: every OAuth client and access token of one account draws on the same allowance, which is separate from the allowance used when you work in the Refersion dashboard. Webhook endpoint requests are limited per account only, so they share one allowance with every other way of managing the account's webhooks, including the dashboard. Read and write requests are counted separately:
| Requests | Limit |
|---|---|
Reads (GET) | 300 per minute |
Writes (POST, PATCH, DELETE) | 60 per minute |
Webhook endpoints (/webhooks/endpoints) | 30 per minute |
Each read limit is shared by every read endpoint, and each write limit by every write endpoint. Each endpoint page states the limit that applies to it.
Requests rejected with 401 Unauthorized also count toward a limit of 300 per minute per bucket.
Successful requests and other error responses do not count toward it. Missing, malformed, expired, and
invalidly signed credentials use a shared bucket per IP address. A valid, unexpired access token uses its
own bucket, even when a later authentication check rejects it, such as after the token is revoked. For
Passport tokens, that separate bucket applies after a successful signature check has been cached; a new
token, or one whose signature needs to be checked again, can receive 429 when other callers on the same
IP address exhaust the shared bucket. Wait for Retry-After before retrying with the same token.
When you exceed a limit the API returns 429 Too Many Requests with a Retry-After header giving the number of seconds until you can retry. Exceeding a read, write, or IP limit returns:
{
"error": "Rate limit of 60 requests per minute exceeded for this account. Retry after 42 seconds.",
"retryAfter": 42
}
Wait for the number of seconds in Retry-After before sending the next request. Webhook endpoint limits return the same status and header, but their body is {"message": "Too Many Attempts."}, so rely on the header rather than the body.