Authentication
All Alianza Experience API calls require an OAuth 2.0 access token sent as Authorization: Bearer <token>. There are two grant types and one optional discovery mechanism.
Authentication uses a dedicated auth base URL ({baseAuthUrl}), separate from the API base URL.
Base URLs
The authentication base URL is fixed per environment, regardless of the end user's phone number or account:
| Environment | Referenced as | URL |
|---|---|---|
| Production API | {baseUrl} |
https://api.alianza.com |
| Sandbox API | {baseUrl} |
https://api.b2.alianza.com |
| Production Auth | {baseAuthUrl} |
https://auth.alianza.com |
| Sandbox Auth | {baseAuthUrl} |
https://auth.beta.alianza.com |
Which grant to use
| Grant | When | Pages |
|---|---|---|
| Client Credentials | Server-to-server calls with no end-user context. Use this for the eligibility check before signup. Always act as an OAuth2 confidential client with this grant. | Client Credentials |
| Authorization Code + PKCE | Acting on behalf of an authenticated user (e.g. creating a connection on their account). | Authorization Code + PKCE |
Private key JWT authentication
Alianza strongly recommends private_key_jwt instead of client_secret when retrieving an access token. It authenticates your client with a short-lived JWT signed by your private key; Alianza validates the signature with the public key registered for your client.
private_key_jwt is the only supported client-authentication method that allows secret rotation with zero downtime. Alianza supports a maximum of two active public keys per client so you can register a new public key, deploy its private key, and then remove the old public key after the transition.
Keep the private key secure and never send it to Alianza. Register only the corresponding public key and provide it to Alianza. Each client assertion must include:
issandsubset to yourclient_id.audset to{baseAuthUrl}/oauth/token.iatset to the current time andexpset to no more than five minutes later.jtiset to a unique identifier to prevent replay.
Include the registered key's ID in the JWT header as kid.
For example, install jose with npm install jose, then generate the assertion from a PKCS#8 PEM private key:
import { randomUUID } from "node:crypto";
import { readFile } from "node:fs/promises";
import { importPKCS8, SignJWT } from "jose";
const clientId = process.env.CLIENT_ID;
const baseAuthUrl = process.env.BASE_AUTH_URL;
const privateKeyPem = await readFile("private-key.pem", "utf8");
const privateKey = await importPKCS8(privateKeyPem, "RS256");
const clientAssertion = await new SignJWT({})
.setProtectedHeader({ alg: "RS256", kid: process.env.KEY_ID })
.setIssuer(clientId)
.setSubject(clientId)
.setAudience(`${baseAuthUrl}/oauth/token`)
.setIssuedAt()
.setExpirationTime("1m")
.setJti(randomUUID())
.sign(privateKey);
Use private_key_jwt when retrieving an access token with Client Credentials and Authorization Code flows.
Token endpoints
| Endpoint | Used by |
|---|---|
POST {baseAuthUrl}/oauth/token |
Client Credentials and Authorization Code grants |
GET {baseAuthUrl}/authorize |
Authorization Code + PKCE only |
Scopes
Scopes are declared in the OpenAPI spec's oauth2 security schemes. The scopes relevant to Experience integrations:
| Scope | Grants permission to |
|---|---|
experience-assignability:check |
Invoke the Experience Assignability endpoint |
experience-connections:manage |
List, retrieve, create, update, and delete Experience Connections for your Experience |
experience-assignments:manage |
List, retrieve, create, and delete Experience Assignments for your Experience |
experience-assignments:read |
Permission to list and retrieve Experience Assignments for your Experience |
experience-connections:read |
Permission to list and retrieve Experience Connections for your Experience |
Token caching and refresh tokens
Access tokens are valid for the duration in expires_in (seconds). Cache them until shortly before they expire rather than requesting a new token per call.
To obtain a refresh token, include the offline_access scope on the /authorize request. The subsequent /oauth/token request then returns a refresh token alongside the access token, letting your Experience get new access tokens without sending the user through sign-in again.
Retrieving a new access token
POST {baseAuthUrl}/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token={refresh_token}