Authorization reference
RocketVerifier Connect follows OAuth 2.0 (RFC 6749) with PKCE (RFC 7636). This page lists every endpoint, parameter and error.
Endpoints
| Endpoint | URL | Spec |
|---|---|---|
| Issuer | https://app.rocketverifier.com | |
| Authorize | https://app.rocketverifier.com/oauth/authorize | RFC 6749 3.1 |
| Token | https://app.rocketverifier.com/api/oauth/token | RFC 6749 3.2 |
| Revoke | https://app.rocketverifier.com/api/oauth/revoke | RFC 7009 |
| Introspect | https://app.rocketverifier.com/api/oauth/introspect | RFC 7662 |
| Dynamic registration | https://app.rocketverifier.com/api/oauth/register | RFC 7591 |
| Server metadata | https://app.rocketverifier.com/.well-known/oauth-authorization-server | RFC 8414 |
| Protected resource metadata | https://api.rocketverifier.com/.well-known/oauth-protected-resource | RFC 9728 |
| API base URL | https://api.rocketverifier.com |
Identifiers and lifetimes
| Value | Prefix | Lifetime | Notes |
|---|---|---|---|
| Client ID | rvapp_ | Permanent | Public. Safe to ship in a browser or mobile app. |
| Client secret | rv_cs_ | Until rotated | Shown once and stored hashed. Rotate in Dashboard > Developer apps. |
| Authorization code | 60 seconds | Single use. A second attempt fails with invalid_grant. | |
| Access token | rv_at_ | 1 hour | Send as Authorization: Bearer. Same header as API keys. |
| Refresh token | rv_rt_ | 90 days | Rotated on every use. Reusing an old one revokes the grant. |
Treat all tokens as opaque strings. Do not parse them; the format after the prefix can change.
GET /oauth/authorize
Redirect the user's browser here. RocketVerifier signs them in, shows the consent screen, then redirects to your redirect_uri.
| Parameter | Required | Description |
|---|---|---|
response_type | Yes | Always code. |
client_id | Yes | Your app's rvapp_... ID. |
redirect_uri | Yes | Must exactly match one of the redirect URIs registered on the app. No wildcards, no prefix matching, and the query string counts. |
scope | Yes | Space separated list, for example verify:bulk jobs:read account:read. See Scopes. |
state | Yes | An unguessable value tied to the user's session. Returned unchanged. Check it on the callback. |
code_challenge | Public clients | BASE64URL(SHA256(code_verifier)), no padding. Recommended for confidential clients too. |
code_challenge_method | With PKCE | Always S256. plain is rejected. |
resource | No | RFC 8707 resource indicator, for example https://api.rocketverifier.com. MCP clients send this. |
prompt | No | consent forces the consent screen even when the user already approved these scopes. |
Consent screen
- The user picks the account to connect: their personal account or an organization where they are owner or admin.
- Each scope is shown in plain language. The user can set an optional monthly credit cap for your app.
- Apps that have not passed review show an Unverified app badge.
- Verified apps skip the screen when the user already granted every requested scope. Unverified apps always show it. Ask for new scopes and the screen lists only what changed.
Success redirect
https://yourapp.com/callback?code=CODE_FROM_REDIRECT&state=3f9c2a8eError redirect
https://yourapp.com/callback?error=access_denied&error_description=The+user+cancelled&state=3f9c2a8e| error | When |
|---|---|
access_denied | The user clicked Cancel on the consent screen. |
invalid_request | A required parameter is missing or malformed, or PKCE is missing for a public client. |
invalid_scope | A scope is unknown or not allowed for this app. |
unauthorized_client | The app is suspended or cannot use this flow. |
unsupported_response_type | response_type is not code. |
server_error | Something failed on our side. Retry later. |
Bad client_id or redirect_uri never redirect
If the client_id is unknown or the redirect_uri does not match, RocketVerifier shows an error page to the user and does not redirect. Redirecting to an unverified URI would leak the code or the error to an attacker.
POST /api/oauth/token
Form-encoded (application/x-www-form-urlencoded). Returns JSON. Never call it from a browser with a client secret.
Client authentication
| Method | How | Use for |
|---|---|---|
client_secret_basic | HTTP Basic: Authorization: Basic base64(client_id:client_secret) | Server apps (recommended) |
client_secret_post | client_id and client_secret in the form body | Server apps that cannot set headers |
none | client_id in the body plus code_verifier (PKCE S256) | Public clients: SPA, mobile, desktop, CLI, MCP |
grant_type=authorization_code
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | authorization_code |
code | Yes | The code from the redirect. Single use, valid 60 seconds. |
redirect_uri | Yes | The same value sent to /oauth/authorize. |
code_verifier | If PKCE was used | The original verifier, 43 to 128 characters. |
curl https://app.rocketverifier.com/api/oauth/token \
-u "rvapp_xxxxxxxxxxxx:rv_cs_xxxxxxxxxxxxxxxx" \
-d grant_type=authorization_code \
-d code=CODE_FROM_REDIRECT \
-d redirect_uri=https://yourapp.com/callback \
-d code_verifier=VERIFIERgrant_type=refresh_token
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | refresh_token |
refresh_token | Yes | The latest rv_rt_... you received. |
Success response
{
"access_token": "rv_at_xxxxxxxxxxxxxxxxxxxxxxxx",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "rv_rt_xxxxxxxxxxxxxxxxxxxxxxxx",
"scope": "verify:bulk jobs:read account:read"
}scope is what the user approved, which can be fewer scopes than you asked for. Check it before you show features that depend on a scope.
Error response
{
"error": "invalid_grant",
"error_description": "The refresh token is invalid, expired or revoked"
}| error | Status | Meaning and what to do |
|---|---|---|
invalid_request | 400 | Missing or duplicated parameter, or wrong content type. Fix the request. |
invalid_client | 401 | Unknown client, wrong secret, or no client authentication. Check your credentials or rotated secret. |
invalid_grant | 400 | Code or refresh token is invalid, expired, already used or revoked; redirect_uri or code_verifier does not match. On refresh: send the user through Connect again. |
unauthorized_client | 400 | The app is suspended or not allowed to use this grant type. |
unsupported_grant_type | 400 | Only authorization_code and refresh_token are supported. |
invalid_scope | 400 | Requested scope is unknown or exceeds what was granted. |
Refresh token rotation and reuse detection
- Every successful refresh returns a new refresh token and invalidates the one you sent.
- If an already used refresh token is presented again, RocketVerifier assumes it was stolen and revokes the whole grant: every access and refresh token for that user and app stops working. Your app receives a
grant.revokedwebhook withreason: "refresh_token_reuse". - Write the new refresh token to storage before you use the new access token, and run only one refresh at a time per connection. See Security best practices.
- A refresh token that is not used for 90 days expires. The user has to connect again.
Calling the API
curl https://api.rocketverifier.com/v1/credits \
-H "Authorization: Bearer rv_at_xxxxxxxxxxxxxxxxxxxxxxxx"| Status | Body error.code | Meaning |
|---|---|---|
| 401 | invalid_token | Access token expired, revoked, or the app was suspended. The response carries WWW-Authenticate: Bearer error="invalid_token". Refresh once, then reconnect if refresh fails. |
| 403 | insufficient_scope | The token is valid but lacks the scope for this endpoint. Ask the user to reconnect with the extra scope. |
| 402 | insufficient_credits | The connected account is out of credits, or your app reached the monthly cap the user set. Tell the user; do not retry. |
| 429 | rate_limited | The connected account's rate limit (the organization's for org connections). Honor Retry-After. |
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token"
{
"error": {
"code": "invalid_token",
"message": "The access token is expired or revoked",
"details": null
}
}See Error Codes and Rate Limits for the shared formats.
POST /api/oauth/revoke
RFC 7009. Revoking a refresh token ends the whole grant (the user is disconnected). Revoking an access token ends only that token. Authenticate the same way as the token endpoint. Always returns 200, even for an unknown token.
curl https://app.rocketverifier.com/api/oauth/revoke \
-u "rvapp_xxxxxxxxxxxx:rv_cs_xxxxxxxxxxxxxxxx" \
-d token=rv_rt_xxxxxxxxxxxxxxxxxxxxxxxx \
-d token_type_hint=refresh_tokenPOST /api/oauth/introspect
RFC 7662. Your app can introspect only tokens issued to it; anything else returns {"active": false}. Client authentication is required.
curl https://app.rocketverifier.com/api/oauth/introspect \
-u "rvapp_xxxxxxxxxxxx:rv_cs_xxxxxxxxxxxxxxxx" \
-d token=rv_at_xxxxxxxxxxxxxxxxxxxxxxxx{
"active": true,
"client_id": "rvapp_xxxxxxxxxxxx",
"scope": "verify:bulk jobs:read account:read",
"token_type": "Bearer",
"exp": 1793534400,
"iat": 1793530800,
"iss": "https://app.rocketverifier.com",
"aud": "https://api.rocketverifier.com"
}POST /api/oauth/register
RFC 7591 dynamic client registration, for MCP clients and other tools that register themselves. It creates public clients only (token_endpoint_auth_method: none, PKCE required). Redirect URIs must be https or a loopback address (http://127.0.0.1, http://localhost). Apps created this way start unverified. For a confidential server app, create it in the dashboard instead. The full request and response are on the MCP connector page.
Server metadata
Discover every value above from https://app.rocketverifier.com/.well-known/oauth-authorization-server (RFC 8414). Libraries that support discovery need only the issuer.
{
"issuer": "https://app.rocketverifier.com",
"authorization_endpoint": "https://app.rocketverifier.com/oauth/authorize",
"token_endpoint": "https://app.rocketverifier.com/api/oauth/token",
"revocation_endpoint": "https://app.rocketverifier.com/api/oauth/revoke",
"introspection_endpoint": "https://app.rocketverifier.com/api/oauth/introspect",
"registration_endpoint": "https://app.rocketverifier.com/api/oauth/register",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "none"],
"revocation_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "none"],
"introspection_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
"scopes_supported": [
"verify:single", "verify:bulk", "jobs:read", "jobs:write", "webhooks:manage",
"credits:read", "monitors:read", "monitors:write", "account:read"
],
"service_documentation": "https://docs.rocketverifier.com/oauth"
}grant.revoked webhook
Sent to the webhook URL set on your app whenever a connection ends. Delete the stored tokens for that grant and show the Connect button again.
{
"event": "grant.revoked",
"event_id": "evt_xxxxxxxxxxxx",
"timestamp": "2026-11-01T12:00:00Z",
"data": {
"grant_id": "4f1d9c2e-...",
"client_id": "rvapp_xxxxxxxxxxxx",
"user_email": "jane@acme.com",
"account_type": "personal",
"organization_id": null,
"reason": "user_disconnected"
}
}| reason | When |
|---|---|
user_disconnected | The user clicked Disconnect in Settings > Connected apps. |
refresh_token_reuse | An already used refresh token was presented. |
Signed with X-RocketVerifier-Signature using your app's webhook secret, the same scheme as every other RocketVerifier webhook. See Verifying Signatures.
