Skip to main content

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.

Save your Client Secret

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 is immediate and irreversible

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_typeLifetimeexpires_in
short_lived1 hour3600
long_lived90 days7776000

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 here

The 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.

Reuse your token until it expires

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 new token replaces the previous one of the same type

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:

RequestsLimit
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.