Authenticate your backend services with Zonos using asymmetric key cryptography — no shared secrets.
Zonos supports machine-to-machine authentication via OAuth 2.0 JWT Bearer Token Grant (RFC 7523). Your service signs a short-lived JWT with your RSA private key; Zonos verifies it using your registered public key and returns a Bearer token scoped to your organization.
Flow summary:
Generate an RSA key pair and register your public key with Zonos.
At runtime, sign a JWT assertion with your private key and POST it to the token endpoint.
Zonos returns a short-lived access token.
Include the access token as Authorization: Bearer <token> on every API request.
Step 1 — Register your public key (one-time setup)
Generate a 4096-bit RSA key pair and share the public key with Zonos. This is done once during onboarding.
Share public_key.pem with Zonos. Store private_key.pem in a dedicated secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) — never in source control or environment variables.
Zonos will register your key and return your Organization ID, which becomes the iss claim in all JWT assertions.
Step 2 — Build a JWT assertion
Sign a JWT with RS256 using your private key. The assertion is valid for a single token exchange — keep the expiry window short (60–300 seconds).
Access tokens expire in 5 minutes by default. Cache the token and refresh proactively — do not request a new token on every API call. Each refresh requires a newly signed JWT assertion.
OAuth 2.0 authentication
OAuth 2.0 authentication
Authenticate your backend services with Zonos using asymmetric key cryptography — no shared secrets.
Zonos supports machine-to-machine authentication via OAuth 2.0 JWT Bearer Token Grant (RFC 7523). Your service signs a short-lived JWT with your RSA private key; Zonos verifies it using your registered public key and returns a Bearer token scoped to your organization.
Flow summary:
Authorization: Bearer <token>on every API request.Step 1 — Register your public key (one-time setup)
Generate a 4096-bit RSA key pair and share the public key with Zonos. This is done once during onboarding.
Generate the key pair
# Generate private keyopenssl genrsa -out private_key.pem 4096# Extract public keyopenssl rsa - private_key.pem -pubout -out public_key.pemShare
public_key.pemwith Zonos. Storeprivate_key.pemin a dedicated secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) — never in source control or environment variables.Zonos will register your key and return your Organization ID, which becomes the
issclaim in all JWT assertions.Step 2 — Build a JWT assertion
Sign a JWT with
RS256using your private key. The assertion is valid for a single token exchange — keep the expiry window short (60–300 seconds).Required claims
iss"org_abc123")sub"checkout-service")aud"zonos-auth"expiatiatjtiThe JWT header must specify
"alg": "RS256"and"typ": "JWT".Code examples
import jwt, uuid, timewith open("private_key.pem") as f:private_key = f.read()now = int(time.time())assertion = jwt.encode({"iss": "org_abc123","sub": "checkout-service","aud": "zonos-auth","iat": now,"exp": now + 300,"jti": str(uuid.uuid4()),},private_key,algorithm="RS256",)Step 3 — Exchange the assertion for an access token
Send the signed JWT to the Zonos token endpoint to receive a short-lived Bearer token.
Endpoint
application/x-www-form-urlencodedis also accepted.Request fields
grant_type"urn:ietf:params:oauth:grant-type:jwt-bearer"assertionRequest and response
{"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer","assertion": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."}access_tokentoken_type"Bearer"expires_inscopeFull code examples
import requestsresponse = requests.post("https://auth.zonos.com/oauth/token",json={"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer","assertion": assertion,},)data = response.json()access_token = data["access_token"]expires_in = data["expires_in"]Step 4 — Call Zonos APIs with the access token
Include the access token as a
Bearertoken in theAuthorizationheader on every Zonos API request.Example request
curl -X POST https://api.zonos.com/graphql \-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \-H "Content-Type: application/json" \-d '{ "query": "{ ... }" }'Token lifecycle and caching
Access tokens expire in 5 minutes by default. Cache the token and refresh proactively — do not request a new token on every API call. Each refresh requires a newly signed JWT assertion.
import time, requests_cache = {"access_token": None, "expires_at": 0}def get_access_token():if time.time() < _cache["expires_at"] - 30:return _cache["access_token"]assertion = build_jwt_assertion()data = requests.post("https://auth.zonos.com/oauth/token",json={"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer","assertion": assertion,},).json()_cache["access_token"] = data["access_token"]_cache["expires_at"] = time.time() + data["expires_in"]return _cache["access_token"]Error reference
All errors follow the OAuth 2.0 error response format (RFC 6749 §5.2):
{"error": "invalid_grant","error_description": "JWT assertion has expired"}error↕400unsupported_grant_typegrant_typewas noturn:ietf:params:oauth:grant-type:jwt-bearer400invalid_request401invalid_grant500server_errorCommon
invalid_grantcauses:expis in the past — ensure your system clock is NTP-synchronizedaudis not exactly"zonos-auth"issdoes not match your registered Organization IDSecurity best practices
jti. A unique value per assertion enables server-side replay detection.access_tokenorassertionvalues. Treat both as credentials.Was this page helpful?