Skip to main content

Overview

Every API request to 88Pay requires a valid authentication token. This guide explains how to generate, cache, and refresh tokens efficiently.
Tokens are short-lived (60 seconds) for security. You’ll need to generate new tokens regularly or implement caching.

Why Tokens?

88Pay uses a two-layer authentication system:
  1. API Credentials (API Key + Merchant ID) - Long-lived, never sent with each request
  2. Access Tokens (JWT) - Short-lived, used for individual API calls
This approach provides:
  • ✅ Enhanced security (credentials not exposed in every request)
  • ✅ Rate limiting and abuse prevention
  • ✅ Session tracking and auditing
  • ✅ Easy credential rotation without disrupting active sessions

Token Generation Endpoint

Endpoint Details

POST https://api-sandbox.88pay.io/api/auth/token

Required Headers

HeaderTypeRequiredDescription
x-api-keystring✅ YesYour API Key from the dashboard
x-merchant-idstring✅ YesYour Merchant ID

No Body Required

This endpoint doesn’t require a request body. Authentication is done via headers only.

Making the Request

curl -X POST "https://api-sandbox.88pay.io/api/auth/token" \
  -H "x-api-key: sk_test_a1b2c3d4e5f6g7h8" \
  -H "x-merchant-id: MCH-COL-TEST-12345"

Response Structure

Success Response (200 OK)

{
  "status": "Success",
  "code": 200,
  "message": "Token generated successfully",
  "data": {
    "access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudF9pZCI6Ik1DSC1DT0wtMjlaVFA0IiwiaWF0IjoxNzM3MTQwNDAwLCJleHAiOjE3MzcxNDA0NjB9.signature...",
    "expires_in": 60,
    "session_id": "sess_53a2cfc4-441e-4554-b560-4e008784d98e"
  }
}

Response Fields

status
string
Status of the request. Always "Success" for 200 responses
code
number
HTTP status code. 200 for successful token generation
message
string
Human-readable message about the operation
data.access_token
string
JWT token to use in the Authorization header of subsequent requestsFormat: Bearer {access_token}
data.expires_in
number
Token lifetime in seconds. Always 60 (1 minute)
data.session_id
string
Unique session identifier. Must be included in the x-session-id headerFormat: sess_{uuid}

Error Responses

Invalid API Key or Merchant ID
    {
      "status": "Error",
      "code": 401,
      "message": "Invalid API key or merchant ID"
    }
Common Causes:
  • Incorrect API Key
  • Incorrect Merchant ID
  • Typos or extra spaces in credentials
  • Using sandbox credentials with production endpoint (or vice versa)
Solution:
  • Verify credentials in the dashboard
  • Check environment variables are loaded correctly
  • Ensure no whitespace in credential values

Token Caching (Best Practice)

Don’t generate a new token for every request! This will hit rate limits and slow down your integration.
Implement token caching to reuse tokens within their 60-second lifetime:

Simple Token Manager

class TokenManager {
  constructor(apiKey, merchantId, baseUrl) {
    this.apiKey = apiKey;
    this.merchantId = merchantId;
    this.baseUrl = baseUrl;
    this.token = null;
    this.sessionId = null;
    this.expiresAt = null;
  }
  
  async getToken() {
    // Return cached token if still valid (with 10s safety margin)
    if (this.token && this.expiresAt && Date.now() < this.expiresAt - 10000) {
      return {
        access_token: this.token,
        session_id: this.sessionId
      };
    }
    
    // Generate new token
    const response = await fetch(`${this.baseUrl}/api/auth/token`, {
      method: 'POST',
      headers: {
        'x-api-key': this.apiKey,
        'x-merchant-id': this.merchantId
      }
    });
    
    if (!response.ok) {
      throw new Error(`Token generation failed: ${response.status}`);
    }
    
    const data = await response.json();
    
    // Cache token (expires in 60s, we cache for 50s)
    this.token = data.data.access_token;
    this.sessionId = data.data.session_id;
    this.expiresAt = Date.now() + 50000; // 50 seconds
    
    return {
      access_token: this.token,
      session_id: this.sessionId
    };
  }
}

// Usage
const tokenManager = new TokenManager(
  process.env.EIGHTY_EIGHT_PAY_API_KEY,
  process.env.EIGHTY_EIGHT_PAY_MERCHANT_ID,
  'https://api-sandbox.88pay.io'
);

// Get token (will cache automatically)
const { access_token, session_id } = await tokenManager.getToken();

// Later... (within 50 seconds, same token is returned)
const { access_token: token2, session_id: sid2 } = await tokenManager.getToken();

Using the Token

Once you have a token, include it in all API requests:

Required Headers

Authorization: Bearer {access_token}
x-session-id: {session_id}
Content-Type: application/json

Example API Request

curl -X POST "https://api-sandbox.88pay.io/api/transactions/charges" \
  -H "Authorization: Bearer eyJhbGciOiJFUzI1NiIs..." \
  -H "x-session-id: sess_53a2cfc4-441e-4554-b560-4e008784d98e" \
  -H "Content-Type: application/json" \
  -d '{
    "flow": "PAYIN",
    "method_code": "CARDS",
    "amount": 50000,
    "currency": "COP",
    "country": "COL",
    "description": "Purchase #123",
    "customer_id": "cust_001",
    "notification_url": "https://yoursite.com/webhook"
  }'

Rate Limits

Token generation has specific rate limits:
LimitSandboxProduction
Per minute10 requests10 requests
Per hour100 requests100 requests
With proper token caching, you should rarely hit these limits. Each cached token can be used for multiple API calls within 50 seconds.

Best Practices

Cache Tokens

Reuse tokens for up to 50 seconds to avoid rate limits

Safety Margin

Refresh tokens 10 seconds before expiry to prevent race conditions

Error Handling

Handle 401 errors by generating a fresh token and retrying

Concurrent Requests

Use a single TokenManager instance across your application

Troubleshooting

Issue: Tokens expire in 60 secondsSolution: Implement token caching as shown above. Each token can be reused for multiple requests within its lifetime.
Issue: Receiving 429 errorsSolution:
  • Implement token caching
  • Don’t generate tokens unnecessarily
  • Use a singleton TokenManager instance
Issue: Token rejected by APISolution:
  • Check token hasn’t expired
  • Verify both Authorization and x-session-id headers are included
  • Generate a new token
Issue: Different credentials for different environmentsSolution: Ensure you’re using production credentials with production URLs

Next Steps