Skip to main content

Overview

88Pay uses a two-layer authentication system to ensure secure API access:
  1. API Credentials (API Key + Merchant ID) - Long-lived identifiers
  2. Access Tokens (JWT) - Short-lived authorization tokens
All API requests must include both a valid Access Token and Session ID in the headers.

Authentication Flow

Integration overview sequence diagram authentication

Step 1: Obtain API Credentials

Get Your API Key and Merchant ID

1

Login to Dashboard

Visit dash.88pay.io and log in with your credentials.
2

Navigate to Settings

Click on Settings in the bottom left of the navigation menu.
3

Upload Required Documents

Go to Account Details and upload all required documents. Your credentials will only be available after document approval.
4

Get API Credentials

Navigate to API Credentials section. You’ll find:
  • API Key: Your pre-generated authentication key
  • Merchant ID: Your unique merchant identifier
Keep your credentials secure! Never expose your API Key in client-side code or public repositories.

Step 2: Generate Access Token

Use your API credentials to generate a short-lived JWT token.

Endpoint

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

Headers

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

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-29ZTP4"

Success Response (200 OK)

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

Error Responses

    {
      "status": "Error",
      "code": 401,
      "message": "Invalid API key or merchant ID"
    }
Cause: Invalid or missing credentials
Solution: Verify your API Key and Merchant ID in the dashboard
    {
      "status": "Error",
      "code": 403,
      "message": "Access denied"
    }
Cause: Account not approved or suspended
Solution: Contact [email protected]
    {
      "status": "Error",
      "code": 429,
      "message": "Rate limit exceeded. Please try again later."
    }
Cause: Too many token generation requests
Solution: Implement token caching and respect rate limits

Step 3: Use Token in API Requests

Include the access token and session ID in all subsequent API requests.

Required Headers

HeaderValueDescription
AuthorizationBearer {access_token}JWT token from auth response
x-session-id{session_id}Session identifier from auth response
Content-Typeapplication/jsonRequired for POST requests

Example 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"
  }'

Token Management

Token Lifecycle

1

Generate

Call /api/auth/token with your credentials
2

Use

Make API requests within 60 seconds
3

Expire

Token automatically expires after 60 seconds
4

Refresh

Generate a new token when needed

Best Practices

Cache Tokens

Store tokens for up to 50 seconds and reuse them for multiple requests

Handle Expiration

Implement automatic token refresh when receiving 401 errors

Secure Storage

Never store credentials in client-side code

Rate Limiting

Don’t generate tokens too frequently (max 10/minute)

Token Caching Example

class TokenManager {
  constructor(apiKey, merchantId) {
    this.apiKey = apiKey;
    this.merchantId = merchantId;
    this.token = null;
    this.sessionId = null;
    this.expiresAt = null;
  }
  
  async getToken() {
    // Return cached token if still valid
    if (this.token && this.expiresAt && Date.now() < this.expiresAt) {
      return {
        access_token: this.token,
        session_id: this.sessionId
      };
    }
    
    // Generate new token
    const response = await fetch('https://api-sandbox.88pay.io/api/auth/token', {
      method: 'POST',
      headers: {
        'x-api-key': this.apiKey,
        'x-merchant-id': this.merchantId
      }
    });
    
    const data = await response.json();
    
    // Cache token (expires in 50 seconds instead of 60 for safety margin)
    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('YOUR_API_KEY', 'YOUR_MERCHANT_ID');

// Tokens are automatically cached and refreshed
const { access_token, session_id } = await tokenManager.getToken();

Security Best Practices

Store credentials in environment variables, never in code:
    # .env
    EIGHTY_EIGHT_PAY_API_KEY=sk_test_a1b2c3d4e5f6g7h8
    EIGHTY_EIGHT_PAY_MERCHANT_ID=MCH-COL-29ZTP4
    // Usage
    const apiKey = process.env.EIGHTY_EIGHT_PAY_API_KEY;
    const merchantId = process.env.EIGHTY_EIGHT_PAY_MERCHANT_ID;
Never expose credentials on the client side:❌ Don’t do this:
    // Client-side code (BAD!)
    const apiKey = 'sk_test_a1b2c3d4e5f6g7h8';
✅ Do this instead:
    // Client makes request to your backend
    const response = await fetch('/api/create-payment', {
      method: 'POST',
      body: JSON.stringify({ amount: 50000 })
    });
    
    // Your backend handles 88Pay authentication
Always use HTTPS for API requests. HTTP requests will be rejected.
Enable IP whitelisting in your dashboard for production environments.
Rotate your API keys every 90 days and immediately if compromised.

Environments

88Pay provides separate credentials for each environment:
Base URL: https://api-sandbox.88pay.io
  • Use for development and testing
  • No real money is transferred
  • Test cards and accounts available
  • Separate credentials from production
    x-api-key: sk_test_...
    x-merchant-id: MCH-COL-TEST...
Production credentials will not work in Sandbox and vice versa. Always use the correct credentials for your environment.

Rate Limits

Token generation is rate-limited to prevent abuse:
Limit TypeSandboxProduction
Requests per minute1010
Requests per hour100100
Implement token caching to stay well below these limits.

Testing Authentication

Use this checklist to verify your authentication setup:
1

Verify Credentials

✅ API Key starts with sk_test_ (sandbox) or sk_live_ (production)
✅ Merchant ID follows format MCH-{COUNTRY}-{ID}
2

Test Token Generation

✅ Successfully generate a token
✅ Receive access_token and session_id
✅ Token expires after 60 seconds
3

Test API Request

✅ Make a request with the token
✅ Include both Authorization and x-session-id headers
✅ Receive successful response
4

Test Error Handling

✅ Handle expired tokens (401)
✅ Handle invalid credentials (401)
✅ Handle rate limiting (429)

Troubleshooting

Problem: Token has expired or is invalidSolution:
  • Generate a new token
  • Check that token hasn’t expired (60s lifetime)
  • Verify you’re using the correct token format
Problem: API Key or Merchant ID is incorrectSolution:
  • Verify credentials in the dashboard
  • Check for typos or extra spaces
  • Ensure you’re using the correct environment credentials
Problem: Session ID not included in requestSolution:
  • Include x-session-id header in all requests
  • Use the session_id from the token generation response
Problem: Too many token generation requestsSolution:
  • Implement token caching
  • Wait before retrying
  • Reduce request frequency

Next Steps