A refresh token lets your app get a new access token without asking the user to log in again. Access tokens expire after 1 hour (expires_in: 3600 seconds). Use this endpoint whenever yours has expired.
If you don't have a refresh token yet, start with Get an Authorization Code.
Important
Each time you use a refresh token, it is invalidated and replaced with a new one. Always store the new
refresh_tokenfrom the response immediately; the old one will no longer work.
Request
Send a POST request to /apiv2/oauth/authorize/token.
Auth type: Basic Auth — Authorization: Basic base64(CLIENT_ID:CLIENT_SECRET)
The Authorization header value must be the Base64 encoding of CLIENT_ID:CLIENT_SECRET (joined by a literal colon). curl's -u flag handles this automatically. If you are constructing the header manually in code or a tool like Postman, encode it first:
echo -n "CLIENT_ID:CLIENT_SECRET" | base64
# → e.g. ZTFiMmMzZDQ6c2VjcmV0
Headers:
Content-Type: application/x-www-form-urlencoded
Body parameters (form-encoded):
| Parameter | Type | Required | Description |
|---|---|---|---|
grant_type | string | Yes | Must be the literal string refresh_token. |
refresh_token | string | Yes | The refresh token you received from a previous token exchange. |
redirect_uri | string | Yes | Must exactly match the URI configured on the App credentials page. |
Using curl (recommended):
curl -X POST https://go.zelt.app/apiv2/oauth/authorize/token \
-u "CLIENT_ID:CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "redirect_uri=https://yourapp.example.com/callback" \
-d "refresh_token=YOUR_SAVED_REFRESH_TOKEN"
Constructing the header manually (Postman, code, etc.):
ENCODED=$(echo -n "CLIENT_ID:CLIENT_SECRET" | base64)
curl -X POST https://go.zelt.app/apiv2/oauth/authorize/token \
-H "Authorization: Basic $ENCODED" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "redirect_uri=https://yourapp.example.com/callback" \
-d "refresh_token=YOUR_SAVED_REFRESH_TOKEN"
Success response
On success, you receive a 200 OK response with this structure:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
Response fields:
| Field | Type | Description |
|---|---|---|
access_token | string | The new access token. Use it in the Authorization: Bearer header for all API calls. |
refresh_token | string | A new refresh token. Save this immediately; the one you just used is now invalid. |
token_type | string | Always Bearer. |
expires_in | integer | Lifetime of the access token in seconds (3600 = 1 hour). |
After a successful exchange:
- Store the new
refresh_tokenimmediately. The token you just used is now invalid. Refresh tokens expire after 90 days of non-use. - Use the new
access_tokenin theAuthorization: Bearerheader for all subsequent API calls. - Repeat this flow whenever your access token expires.
Common errors
| Error | Cause | Fix |
|---|---|---|
401 Unauthorized | Refresh token expired (after 90 days), already used, or malformed | Restart the full authorization code flow to get a new token pair |
401 Unauthorized | Wrong Client ID or Client secret | Copy them again from the App credentials page |
401 Unauthorized | Authorization header value is not Base64-encoded (e.g. sent as Basic CLIENT_ID:CLIENT_SECRET literally) | The value after Basic must be Base64-encoded. Use curl -u "CLIENT_ID:CLIENT_SECRET" (encodes automatically), or encode manually: |
echo -n "CLIENT_ID:CLIENT_SECRET" | base64 | ||
401 Unauthorized | redirect_uri missing or doesn't match what's configured in the app | Include redirect_uri in the request body and ensure it exactly matches the URI on your App credentials page |
400 Bad Request | grant_type missing or incorrect | Ensure grant_type=refresh_token is included in the form body |
Important
If your refresh token is lost or expired, you must restart the full authorization code flow on the Get an authorization code page.