Authentication
Bearer JWT tokens, scopes, refresh token rotation, and OIDC client setup
Authentication
505pay uses Bearer JWTs issued by the /token endpoint. Tokens are short-lived and rotate on refresh.
Token vs. API key
Use OAuth /token for console / dashboard / OIDC flows where a human or admin app is acting on a user's behalf. Use a long-lived API key (sk_test_… / sk_live_…) for server-to-server merchant integrations like checkout intent creation and refunds. See the API keys guide for the API-key path.
How it works
- Your OIDC client presents
client_id+client_secret(or user credentials) to/token. - The server returns an
access_token(JWT) and arefresh_token. - Include the access token in every API request as
Authorization: Bearer <token>. - When the access token expires, use the refresh token to obtain a new pair. Refresh tokens are single-use — each use rotates to a new token.
Token endpoint
POST https://sandbox.505pay.link/token
Content-Type: application/x-www-form-urlencodedClient credentials grant
Use this grant for server-to-server integrations where no end-user is involved.
curl -X POST https://sandbox.505pay.link/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET"const params = new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.CLIENT_ID!,
client_secret: process.env.CLIENT_SECRET!,
});
const res = await fetch('https://sandbox.505pay.link/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params,
});
const { access_token, refresh_token, expires_in } = await res.json();import requests
r = requests.post('https://sandbox.505pay.link/token', data={
'grant_type': 'client_credentials',
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
})
data = r.json()
access_token = data['access_token']
refresh_token = data['refresh_token']
expires_in = data['expires_in']Password grant
Use this grant when acting on behalf of a specific user.
curl -X POST https://sandbox.505pay.link/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "username=$USER_EMAIL" \
-d "password=$USER_PASSWORD"Token response
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "def5..."
}| Field | Description |
|---|---|
access_token | JWT to include in Authorization: Bearer header |
token_type | Always Bearer |
expires_in | Seconds until the access token expires (configurable per tenant) |
refresh_token | Single-use token for obtaining a new access token pair |
Refreshing tokens
When your access token expires, use the refresh token to obtain a new pair. The old refresh token is immediately invalidated — store the new one.
curl -X POST https://sandbox.505pay.link/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "refresh_token=$REFRESH_TOKEN"Refresh tokens are single-use. Using a refresh token more than once (replay attack) immediately invalidates the entire token family, requiring the user to re-authenticate.
Scopes
Scopes control what the access token is permitted to do. Request scopes using the scope parameter in your token request.
-d "scope=payments:read payments:write webhooks:manage"| Scope | Description |
|---|---|
payments:read | List and retrieve payment records |
payments:write | Create and update payments |
webhooks:manage | Register and manage webhook subscriptions |
refunds:write | Issue refunds |
reports:read | Access settlement and reconciliation reports |
admin | Full administrative access (tenant admin only) |
OIDC client setup
- Log in to dashboard.505pay.link.
- Navigate to Settings → API Credentials → New Client.
- Choose Confidential (server-side) or Public (mobile / SPA) client type.
- Copy
CLIENT_IDandCLIENT_SECRET— the secret is shown once at creation. - Store credentials in environment variables, never in source code.
Token lifetime is configurable per tenant. Contact support to adjust the default if your use case requires longer-lived tokens.
Using the token
Pass the access token in the Authorization header on every request:
Authorization: Bearer eyJ...Tokens older than expires_in seconds return 401 Unauthorized with error code token_expired. Refresh proactively rather than reactively to avoid dropped requests.
Was this page helpful?