Authentication
Overview
The BaaS API uses OAuth 2.0 Client Credentials Flow for server-to-server authentication. All endpoints (except /v1/authentication/oauth/access-token) require a valid Bearer token in the Authorization header.
Key Facts:
- Tokens are JWT-based with a default validity of 1 hour
- Token refresh is automatic when close to expiration
- Scopes control which operations your client can perform
- Rate limiting: max 100 token requests per minute per client
Setup — Obtaining Client Credentials
To integrate with the BaaS API, you need Client_ID and Client_Secret.
Requesting Credentials
Contact Support: Email
suporte@axiadigitalsolutions.comwith your integration details:- Company name
- Integration purpose (e.g., "Payroll Platform", "Accounting Integration")
- Expected transaction volume
- List of required scopes (see Scopes & Permissions)
Axia Setup: Our team will:
- Create credentials in BackOffice
- Provide them via secure link (valid 24 hours)
- Document assigned scopes
Store Securely: Save credentials in environment variables or vault:
# .env (never commit this file)
BAAS_CLIENT_ID=axia_prod_abc123xyz
BAAS_CLIENT_SECRET=sk_live_super_secret_never_commit# Or use a vault (recommended for production)
vault kv put secret/baas \
client_id=axia_prod_abc123xyz \
client_secret=sk_live_super_secret_never_commit⚠️ Security Reminder: Never share or commit Client_Secret. Each integrator has unique credentials tied to their tenant context.
POST /v1/authentication/oauth/access-token
Request a Bearer token for API access using Client Credentials.
Description
This endpoint exchanges your Client_ID and Client_Secret for a signed JWT token valid for 1 hour. The token grants access to all API operations within your assigned scopes.
Headers
| Header | Value |
|---|---|
Authorization | Basic <Base64(Client_ID:Client_Secret)> |
Content-Type | application/json |
Request
No body required.
Example
Step 1: Encode credentials in Base64
CREDS=$(echo -n "$BAAS_CLIENT_ID:$BAAS_CLIENT_SECRET" | base64)
echo $CREDS
# Output: YXhpYV9wcm9kX2FiYzEyM3h5ejo6c2tfbGl2ZV9zdXBlcl9zZWNyZXQ=Step 2: Request token
curl -X POST https://baas-gtw.axiadigitalsolutions.com/v1/authentication/oauth/access-token \
-H "Authorization: Basic $CREDS" \
-H "Content-Type: application/json"Response 200 OK
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im9hdXRoMjAyNDAzMTUifQ.eyJzdWIiOiJheGlhX3Byb2RfYWJjMTIzeHl6IiwiY2xpZW50X2lkIjoiYXhpYV9wcm9kX2FiYzEyM3h5eiIsImV4cCI6MTcxMDc4NjQwMCwiaWF0IjoxNzEwNzgyODAwLCJzY29wZSI6ImFjY291bnRzOnJlYWQgYWNjb3VudHM6d3JpdGUgcGl4OnNlbmQgd2ViaG9va3M6bWFuYWdlIn0.xyz...",
"token_type": "Bearer",
"expires_in": 3600,
"issued_at": "2024-03-15T10:00:00Z",
"scope": "accounts:read accounts:write pix:send webhooks:manage"
}Response 401 Unauthorized
{
"error": "E00101",
"message": "Invalid client credentials",
"timestamp": "2024-03-15T10:00:05Z"
}Response 429 Too Many Requests
{
"error": "E00102",
"message": "Rate limit exceeded: max 100 token requests per minute",
"retry_after": 30
}Token Refresh & Caching
When to Refresh
Tokens expire after 1 hour. To avoid interruptions, refresh 5 minutes before expiration rather than waiting for expiration errors.
Strategy: Preventive Refresh
Check the expires_in field and schedule a refresh when:
- Current time + 5 minutes >= token expiration time
Node.js / TypeScript Example
class BaaSClient {
private token: string | null = null;
private expiresAt: number = 0;
private readonly clientId: string;
private readonly clientSecret: string;
private readonly baseUrl: string =
'https://baas-gtw.axiadigitalsolutions.com';
constructor(clientId: string, clientSecret: string) {
this.clientId = clientId;
this.clientSecret = clientSecret;
}
private getBasicAuth(): string {
const creds = `${this.clientId}:${this.clientSecret}`;
return Buffer.from(creds).toString('base64');
}
async getToken(): Promise<string> {
// Return cached token if still valid (with 5-min buffer)
const bufferMs = 5 * 60 * 1000;
if (this.token && Date.now() < this.expiresAt - bufferMs) {
return this.token;
}
// Request new token
const response = await fetch(
`${this.baseUrl}/v1/authentication/oauth/access-token`,
{
method: 'POST',
headers: {
'Authorization': `Basic ${this.getBasicAuth()}`,
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(
`Token request failed: ${response.status} ${response.statusText}`
);
}
const data = await response.json();
this.token = data.access_token;
// Calculate absolute expiration timestamp
this.expiresAt = Date.now() + data.expires_in * 1000;
return this.token;
}
async makeRequest<T>(
method: string,
path: string,
body?: unknown
): Promise<T> {
const token = await this.getToken();
const response = await fetch(`${this.baseUrl}${path}`, {
method,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok) {
throw new Error(
`API request failed: ${response.status} ${response.statusText}`
);
}
return response.json() as Promise<T>;
}
}
// Usage
const client = new BaaSClient(
process.env.BAAS_CLIENT_ID!,
process.env.BAAS_CLIENT_SECRET!
);
// First call fetches token
const accounts = await client.makeRequest<Account[]>(
'GET',
'/v1/accounts'
);
// Second call reuses token (still valid)
const account = await client.makeRequest<Account>(
'GET',
'/v1/accounts/acc_123'
);
// After 55 minutes, next call automatically refreshesPython Example
import os
import base64
import time
import requests
from datetime import datetime
class BaaSClient:
def __init__(self, client_id: str, client_secret: str):
self.client_id = client_id
self.client_secret = client_secret
self.base_url = "https://baas-gtw.axiadigitalsolutions.com"
self.token = None
self.expires_at = 0
def get_basic_auth(self) -> str:
creds = f"{self.client_id}:{self.client_secret}"
encoded = base64.b64encode(creds.encode()).decode()
return encoded
def get_token(self) -> str:
# Check if cached token is still valid (5-min buffer)
buffer_seconds = 5 * 60
if self.token and time.time() < self.expires_at - buffer_seconds:
return self.token
# Request new token
response = requests.post(
f"{self.base_url}/v1/authentication/oauth/access-token",
headers={
"Authorization": f"Basic {self.get_basic_auth()}",
"Content-Type": "application/json",
},
)
response.raise_for_status()
data = response.json()
self.token = data["access_token"]
self.expires_at = time.time() + data["expires_in"]
return self.token
def request(self, method: str, path: str, json=None):
token = self.get_token()
response = requests.request(
method,
f"{self.base_url}{path}",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
},
json=json,
)
response.raise_for_status()
return response.json()
# Usage
client = BaaSClient(
os.getenv("BAAS_CLIENT_ID"),
os.getenv("BAAS_CLIENT_SECRET"),
)
accounts = client.request("GET", "/v1/accounts")
print(accounts)POST /v1/authentication/oauth/introspect
Validate a token and retrieve its metadata (active status, expiration, scopes).
Description
Use this endpoint to check if a token is still active before making API calls. Useful for debugging or validating token state in audit logs.
Headers
| Header | Value |
|---|---|
Authorization | Basic <Base64(Client_ID:Client_Secret)> |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Bearer token value |
Example
CREDS=$(echo -n "$BAAS_CLIENT_ID:$BAAS_CLIENT_SECRET" | base64)
TOKEN="eyJhbGciOiJSUzI1NiIs..."
curl -X POST "https://baas-gtw.axiadigitalsolutions.com/v1/authentication/oauth/introspect?token=$TOKEN" \
-H "Authorization: Basic $CREDS" \
-H "Content-Type: application/json"Response 200 OK (Active Token)
{
"active": true,
"client_id": "axia_prod_abc123xyz",
"token_type": "Bearer",
"exp": 1710786400,
"iat": 1710782800,
"issued_at": "2024-03-15T10:00:00Z",
"expires_at": "2024-03-15T11:00:00Z",
"scope": "accounts:read accounts:write pix:send webhooks:manage"
}Response 200 OK (Expired Token)
{
"active": false,
"reason": "Token expired on 2024-03-15T11:00:00Z"
}Response 200 OK (Revoked Token)
{
"active": false,
"reason": "Token was revoked"
}POST /v1/authentication/oauth/revoke
Revoke a token and invalidate all associated grants immediately.
Description
Use this endpoint to logout a client or rotate credentials. Revoked tokens cannot be reused, and the next API call will require a fresh token.
Headers
| Header | Value |
|---|---|
Authorization | Basic <Base64(Client_ID:Client_Secret)> |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Bearer token value |
Example
CREDS=$(echo -n "$BAAS_CLIENT_ID:$BAAS_CLIENT_SECRET" | base64)
TOKEN="eyJhbGciOiJSUzI1NiIs..."
curl -X POST "https://baas-gtw.axiadigitalsolutions.com/v1/authentication/oauth/revoke?token=$TOKEN" \
-H "Authorization: Basic $CREDS" \
-H "Content-Type: application/json"Response 200 OK
{
"status": "Token revoked successfully",
"revoked_at": "2024-03-15T10:30:00Z",
"token_id": "jti_xyz123"
}Response 404 Not Found
{
"error": "E00103",
"message": "Token not found or already revoked"
}Scopes & Permissions
Scopes define which operations your client can perform. Axia assigns scopes based on your integration needs at setup time.
| Scope | Description | Endpoints |
|---|---|---|
accounts:read | Read account information | GET /v1/accounts, /checkOnboarding |
accounts:write | Create and edit accounts | POST /v1/bank-accounts/create |
pix:send | Send PIX payments | POST /v1/pix/payment |
pix:register | Register PIX keys | POST /v1/pix/register |
bills:read | Read billing data | GET /v1/bills |
bills:write | Create and manage bills | POST /v1/bills/create |
webhooks:manage | Register and manage webhooks | POST /v1/webhooks |
transfers:send | Send domestic transfers (TED) | POST /v1/transfers |
cards:read | Read card information | GET /v1/cards |
cards:write | Issue and manage cards | POST /v1/cards/issue |
Note: Scopes are assigned per client and cannot be changed at runtime. Contact support to modify your scope permissions.
Error Handling
Common HTTP Status Codes
| Status | Error Code | Meaning | Action |
|---|---|---|---|
| 200 | — | Success | Process response |
| 400 | E00100 | Invalid request format | Check headers and query parameters |
| 401 | E00101 | Invalid credentials or expired token | Validate Client_ID/Secret, refresh |
| 403 | E00102 | Insufficient scopes for requested operation | Contact support to enable scopes |
| 429 | E00103 | Rate limit exceeded | Retry after Retry-After seconds |
| 500 | E00500 | Internal server error | Retry with exponential backoff |
| 504 | E00504 | Gateway timeout | Retry with exponential backoff (max 3) |
Example Error Response
{
"error": "E00101",
"message": "Token has expired",
"error_description": "The access token is no longer valid. Please request a new token.",
"timestamp": "2024-03-15T10:35:00Z"
}Best Practices
Do's ✅
- Cache tokens in memory between requests (reduces load and latency)
- Refresh tokens proactively (5 minutes before expiration)
- Use environment variables or vault for storing credentials
- Log authentication errors (401, 403) for debugging
- Implement exponential backoff for retries (500, 504 errors)
- Store token expiration timestamp and check before each API call
- Rotate credentials periodically (e.g., quarterly) for security
Don'ts ❌
- Never log or print tokens to console or files
- Never commit credentials to version control (use .gitignore)
- Never share Client_Secret across teams or environments
- Never hardcode credentials in application code
- Never trust token content without validating signature (use introspect)
- Never retry immediately on 401 errors (likely a permanent credential issue)
- Never store tokens in localStorage or client-side storage (backend only)
Recommended Architecture
┌─────────────────────┐
│ Your Application │
│ (Server-side) │
└──────────┬──────────┘
│
┌────▼─────────────────┐
│ BaaS Client Library │
│ (Token Cache + Auto │
│ Refresh + Retries) │
└────┬──────────────────┘
│
┌────▼─────────────────┐
│ BaaS API Gateway │
│ (Validation + │
│ Scope Check) │
└─────────────────────┘- Keep credentials in a secure vault (HashiCorp Vault, AWS Secrets Manager, etc.)
- Use a client library (Node.js example above) to handle token refresh automatically
- Never expose credentials in logs or error messages
- Use HTTPS only for all API communications