This walks through the full authorization-code grant for a private (confidential) app — the most common case. For browser-only and mobile apps, follow this guide but use PKCE instead of a client_secret.
1. Create the app
Open the OAuth apps settings
Go to https://app.revroute.ru/<workspace>/settings/oauth-apps and click Create OAuth app.
Fill in the basics
- Name — shown on the consent screen
- Homepage URL — your marketing page
- Redirect URIs — one or more exact URLs RevRoute is allowed to redirect users back to. Use
https://except forhttp://localhost:*in development. - Client type —
private(with secret) orpublic(PKCE only)
Save the credentials
Copy the client_id and, for private apps, the client_secret. The secret is shown only once — store it in your secrets manager.
2. Send the user to the authorize endpoint
When a user clicks “Connect RevRoute” in your app, redirect them to:
https://app.revroute.ru/oauth/authorize
?client_id=dub_app_xxx
&redirect_uri=https%3A%2F%2Fyourapp.com%2Foauth%2Fcallback
&response_type=code
&scope=links.read+links.write+analytics.read
&state=<random-csrf-token>| Param | Required | Notes |
|---|---|---|
client_id | yes | From the dashboard |
redirect_uri | yes | Must match exactly one of the registered URIs |
response_type | yes | Always code |
scope | yes | Space- or +-separated scopes — see Scopes |
state | strongly recommended | Random per-request token for CSRF protection. Verify it on the callback. |
RevRoute shows the user a consent screen listing the scopes you requested. After they click Authorize, RevRoute redirects to your redirect_uri with a short-lived code:
https://yourapp.com/oauth/callback?code=AbCd1234...&state=...If the user denies, you get ?error=access_denied&state=... instead.
3. Exchange the code for tokens
The code is valid for 2 minutes and can only be used once. Exchange it server-side:
curl -X POST https://api.revroute.ru/oauth/token \
-u "$REVROUTE_CLIENT_ID:$REVROUTE_CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code=AbCd1234..." \
--data-urlencode "redirect_uri=https://yourapp.com/oauth/callback"A successful response:
{
"access_token": "dub_access_token_xxx",
"token_type": "Bearer",
"expires_in": 7200,
"refresh_token": "yyy",
"scope": "links.read links.write analytics.read"
}Store both tokens against the user’s account in your database.
4. Call the API
curl https://api.revroute.ru/links \
-H "Authorization: Bearer dub_access_token_xxx"The access token works exactly like a personal API key — except it’s scoped to what the user authorized.
5. Refresh when it expires
When you receive 401 invalid_token, exchange the refresh token for a new pair:
curl -X POST https://api.revroute.ru/oauth/token \
-u "$REVROUTE_CLIENT_ID:$REVROUTE_CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=refresh_token" \
--data-urlencode "refresh_token=yyy"The response contains a new refresh_token — the old one is now invalid. Always persist the latest one immediately after the response, before doing any other work.
Refresh-token rotation prevents replay attacks. If two clients ever try to use the same refresh token, RevRoute revokes the entire authorization and the user has to re-authorize.
6. Identify the user (optional)
curl -X POST https://api.revroute.ru/oauth/userinfo \
-H "Authorization: Bearer dub_access_token_xxx"{
"id": "user_42",
"email": "alice@example.com",
"name": "Alice",
"image": "https://...",
"workspace": {
"id": "ws_3KmZ",
"slug": "acme",
"name": "Acme"
}
}This requires the implicit user.read scope (always granted, no need to request it).
Errors
The token endpoint returns standard OAuth error responses. See API errors for the general shape; OAuth-specific codes include invalid_grant (bad/expired code or rotated refresh token), invalid_client (wrong client_id/client_secret), and invalid_redirect_uri (mismatch with the registered list).