Skip to content

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

  1. Contact Support: Email suporte@axiadigitalsolutions.com with your integration details:

    • Company name
    • Integration purpose (e.g., "Payroll Platform", "Accounting Integration")
    • Expected transaction volume
    • List of required scopes (see Scopes & Permissions)
  2. Axia Setup: Our team will:

    • Create credentials in BackOffice
    • Provide them via secure link (valid 24 hours)
    • Document assigned scopes
  3. Store Securely: Save credentials in environment variables or vault:

bash
# .env (never commit this file)
BAAS_CLIENT_ID=axia_prod_abc123xyz
BAAS_CLIENT_SECRET=sk_live_super_secret_never_commit
bash
# 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

HeaderValue
AuthorizationBasic <Base64(Client_ID:Client_Secret)>
Content-Typeapplication/json

Request

No body required.

Example

Step 1: Encode credentials in Base64

bash
CREDS=$(echo -n "$BAAS_CLIENT_ID:$BAAS_CLIENT_SECRET" | base64)
echo $CREDS
# Output: YXhpYV9wcm9kX2FiYzEyM3h5ejo6c2tfbGl2ZV9zdXBlcl9zZWNyZXQ=

Step 2: Request token

bash
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

json
{
  "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

json
{
  "error": "E00101",
  "message": "Invalid client credentials",
  "timestamp": "2024-03-15T10:00:05Z"
}

Response 429 Too Many Requests

json
{
  "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

typescript
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 refreshes

Python Example

python
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

HeaderValue
AuthorizationBasic <Base64(Client_ID:Client_Secret)>

Query Parameters

ParameterTypeRequiredDescription
tokenstringYesBearer token value

Example

bash
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)

json
{
  "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)

json
{
  "active": false,
  "reason": "Token expired on 2024-03-15T11:00:00Z"
}

Response 200 OK (Revoked Token)

json
{
  "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

HeaderValue
AuthorizationBasic <Base64(Client_ID:Client_Secret)>

Query Parameters

ParameterTypeRequiredDescription
tokenstringYesBearer token value

Example

bash
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

json
{
  "status": "Token revoked successfully",
  "revoked_at": "2024-03-15T10:30:00Z",
  "token_id": "jti_xyz123"
}

Response 404 Not Found

json
{
  "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.

ScopeDescriptionEndpoints
accounts:readRead account informationGET /v1/accounts, /checkOnboarding
accounts:writeCreate and edit accountsPOST /v1/bank-accounts/create
pix:sendSend PIX paymentsPOST /v1/pix/payment
pix:registerRegister PIX keysPOST /v1/pix/register
bills:readRead billing dataGET /v1/bills
bills:writeCreate and manage billsPOST /v1/bills/create
webhooks:manageRegister and manage webhooksPOST /v1/webhooks
transfers:sendSend domestic transfers (TED)POST /v1/transfers
cards:readRead card informationGET /v1/cards
cards:writeIssue and manage cardsPOST /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

StatusError CodeMeaningAction
200SuccessProcess response
400E00100Invalid request formatCheck headers and query parameters
401E00101Invalid credentials or expired tokenValidate Client_ID/Secret, refresh
403E00102Insufficient scopes for requested operationContact support to enable scopes
429E00103Rate limit exceededRetry after Retry-After seconds
500E00500Internal server errorRetry with exponential backoff
504E00504Gateway timeoutRetry with exponential backoff (max 3)

Example Error Response

json
{
  "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)
┌─────────────────────┐
│  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