Skip to main content
The partner caller (machine-to-machine) authenticates with one of two equivalent schemes: a Bearer API key (recommended) or an HMAC-SHA256 signature. The same routes accept either one. Separately, your backend exchanges a user identifier for a short-lived end-user JWT that unlocks the payment operations.
Partner credentials (partner id, API key or HMAC secret, webhook secret) are provisioned by our team at onboarding. Request them at pag.finance/businesses.
Send the key directly in the Authorization header on every request. No canonical string, no signature, no nonce, no timestamp.
1

Generate a key

Create a key in the dashboard (API and Integrations panel) or via POST /api/v1/partners/me/api-keys. The full sk_live_... value is shown once only; store it, because it is not recoverable (only the hash is kept).
2

Send it on every request

Attach the key exactly as received in the Authorization header.
3

Rotate or revoke

List keys with GET /api/v1/partners/me/api-keys and revoke one with POST /api/v1/partners/me/api-keys/:keyId/revoke (takes effect immediately). Each partner can hold several keys, for example one per environment or service.
Always transit the key over HTTPS. The key is a bearer secret: if it leaks, revoke it. Partner status (SUSPENDED or REVOKED) and the optional IP allowlist still apply. An invalid or revoked key returns a generic 401.

HMAC-SHA256 - legacy and advanced

Use HMAC if you already have an HMAC integration or need nonce-based anti-replay. The header carries the partner id, a timestamp, a nonce, and the signature.
The signature is computed over a canonical string with a literal newline separator between fields:
1

Derive the signing key

signingKey = SHA256(rawSecret + ":" + partnerId). The raw secret never travels and is never stored: only SHA256(secret:partnerId) is kept. The secret is plain text, not base64.
2

Hash the body

bodyHash = SHA256_hex( JSON.stringify(JSON.parse(rawBody)) ). The body is normalized before hashing. For a GET or an empty body, hash the normalized empty body.
3

Sign the canonical string

signature = HMAC_SHA256_hex(signingKey, canonical) (64 hex chars).
4

Build the header

HMAC-SHA256 partnerId=...,timestamp=...,nonce=...,signature=...
Timestamp window: 300 seconds (5 minutes); outside it, 401. Anti-replay nonce: deduplicated in Redis for 600 seconds (10 minutes); reuse within that window returns 401. Use a unique nonce per request.

End-user JWT

Payment operations (cash-out, cash-in, receipts) run as an end user, not as the partner. Your backend exchanges the user’s pubkey or uid for a JWT.

Issue a token

POST /api/v1/auth/token, authenticated by the partner (API key or HMAC).
string
required
Partner credentials: Bearer sk_live_... or the HMAC header.
string
The user’s blockchain address. Provide pubkey or uid (at least one, minimum 3 characters).
string
The user’s internal id, as an alternative to pubkey.
string
default:"config default"
Optional TTL override, for example 1h or 7d. Capped at 30d.
string
The signed JWT (HS256). Send it as Authorization: Bearer <token> on protected routes.
string
The effective TTL applied to the token.
string
Always Bearer.
object
{ pubkey, kycStatus, partnerId } for the resolved user.
The token carries identity only (pubkey, uid, partnerId, iss). Issuance refuses a BLOCKED user with 403. The per-operation KYC gate (kycStatus === APPROVED) is enforced on the protected routes, not at issuance. A token minted by another instance is rejected with 401 (the issuer is validated on every call).

Token usage

Include the end-user JWT in the Authorization header of every authenticated user call:
On a 401, request a new token from POST /api/v1/auth/token. Tokens are short-lived; do not cache them past expiresIn.