Refreshing & revoking
Access tokens are short-lived. Use the refresh grant to stay authorized, understand rotation and reuse-detection, and revoke tokens when access should end.
Token types
| Token | Prefix | TTL | Notes |
|---|---|---|---|
| Access token | zpat_ | 1 hour (3600s) | Bearer credential for all /dev/v1 calls. |
| Refresh token | zprt_ | 30 days | Exchanges for a fresh access token. Rotates on every use. |
| Authorization code | zpac_ | 5 minutes | Single-use; exchanged once at the token endpoint. |
Only sha256 hashes of tokens, codes, and secrets are ever stored server-side — the raw values exist only in transit and in your storage.
The refresh grant
When the access token nears expiry, exchange the refresh token for a new pair using grant_type=refresh_token. Confidential clients also authenticate with their client_secret.
curl -X POST https://api.zetpin.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=refresh_token \
-d refresh_token=zprt_1a2b3c4d... \
-d client_id=zp_app_xxx \
-d client_secret=zp_sec_xxx{
"access_token": "zpat_new0token...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "zprt_new0refresh...",
"scope": "profile.read posts.write posts.read"
}Rotation & reuse detection
Every refresh returns a new refresh token and invalidates the one you just used. Always persist the new zprt_ from each response and discard the old one.
Reuse revokes everything
Refresh tokens rotate on every use. If an already-used (rotated-out) refresh token is presented again, ZetPin treats it as a replay attack and revokes all of that app + user's grants immediately — the user has to re-authorize. This is why you must store the latest refresh token atomically and never fire two refreshes with the same token concurrently.Revoking a token
To end access — for example when a user disconnects your integration — POST the token to /oauth/revoke (RFC 7009). You can revoke either an access or a refresh token.
curl -X POST https://api.zetpin.com/oauth/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d token=zprt_1a2b3c4d... \
-d client_id=zp_app_xxx \
-d client_secret=zp_sec_xxxRevocation responds with 200 OK whether or not the token was still valid — per RFC 7009, revoking an unknown or already-invalid token is not an error.
Storage best practices
- Store tokens encrypted at rest, server-side where possible. Never expose a
client_secretor refresh token to a browser or mobile client. - Persist the rotated refresh token from every response before your next request, in a single atomic write, to avoid losing it on a crash.
- Serialize refreshes per user — concurrent refreshes with the same token trip reuse detection and revoke the grant.
- Refresh proactively (e.g. shortly before the 1-hour expiry) rather than waiting for a
401 invalid_token. - Revoke tokens on user disconnect, logout, or credential rotation.
Related
A revoked or expired access token returns401 invalid_token with a WWW-Authenticate header — see Errors.