Authentication
Authenticated endpoints need a short-lived access token. You get one by signing a JWT — the client assertion — with your private key and exchanging it for a token. You then send that token on every request.
There is no API key and no shared secret in the request. Your identity is proven by a signature only you can produce.
One-time setup
Generate an RSA key pair. Give AliX the public key during onboarding and keep the private key secret — it never leaves your server.
AliX also allowlists the server IPs you call from. Requests from any other IP are rejected with
PARTNER_IP_NOT_ALLOWED (HTTP 403), no matter how valid the token is.
Step 1 — Build the client assertion
Sign a JWT with RS256 using your private key. Use a standard JWT library; do not hand-roll the encoding.
| Claim | Value |
|---|---|
iss | Your partner code. AliX picks your public key by this. |
aud | The literal string alixpay-v3-token. |
iat | Issue time, as a Unix timestamp in seconds. |
jti | A unique id for this assertion. Recommended — it lets AliX reject replays. |
exp | Optional short expiry, Unix seconds. |
The assertion is accepted only briefly after iat, so sign a fresh one for each token request
instead of caching it. A bad signature, a wrong aud, or a stale assertion returns
INVALID_SIGNATURE (HTTP 401).
Step 2 — Exchange it for a token
POST /v3/auth/tokens with the assertion and the email of the user you are acting for:
The data payload returns accessToken and expiresAt (Unix seconds).
Access tokens are valid for 60 minutes and there is no refresh token. When a token nears its
expiresAt, repeat this handshake for a new one.
See Issue an access token for the full contract and runnable code samples.
Step 3 — Call the API
Send the token on every request:
Authorization: Bearer <accessToken>
A missing, invalid, or expired token returns ACCESS_TOKEN_INVALID (HTTP 401).
One token, one user
A token names the user it acts for. Everything you do with it — KYC, quotes, orders — happens on behalf of that user. If you serve many customers, hold one token per customer, not one token for your platform.
Endpoints that need no token
- The token exchange itself,
POST /v3/auth/tokens. - QR decoding,
POST /v3/scan-to-pay/qr-decodings. - Service metadata,
GET /v3/public/version.
The IP allowlist still applies to all of them.
Example
import jwt from 'jsonwebtoken';
import {randomUUID} from 'node:crypto';
import {readFileSync} from 'node:fs';
const privateKey = readFileSync('alix-private.pem', 'utf8');
function clientAssertion() {
return jwt.sign(
{
iss: 'YOUR_PARTNER_CODE',
aud: 'alixpay-v3-token',
iat: Math.floor(Date.now() / 1000),
jti: randomUUID(),
},
privateKey,
{algorithm: 'RS256'},
);
}
export async function accessTokenFor(userEmail) {
const res = await fetch('https://sandbox.alixpay.com/v3/auth/tokens', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({clientAssertion: clientAssertion(), userEmail}),
});
const {code, message, data} = await res.json();
if (code !== 'SUCCESS') throw new Error(`${code}: ${message}`);
// Cache it against data.expiresAt (Unix seconds) — it lives 60 minutes.
return data.accessToken;
}