Skip to content

Authentication

EasySlip API uses HMAC-SHA256 Signature Authentication for secure API access. Every request must be signed with a Secret Key to verify authenticity and data integrity.

Required Credentials

When you create an application, you will receive 3 credentials:

CredentialFormatPurposeWhere to get it
Partners API Key64-char hex stringIdentifies your client, sent as X-API-Key on every requestCreated in application settings
Secret Key64-char hex stringSigns every request with HMAC-SHA256Shown once when creating a Partners Client
Branch KeyUUID v4Identifies the Branch, sent as X-Branch-Key for endpoints that require a branchAuto-generated when creating a new Branch

Quick Summary

  • Every request -> Send Partners API Key as X-API-Key and sign with Secret Key
  • Verify slips / manage a specific branch? -> Also send Branch Key as X-Branch-Key

DANGER

Never expose your Secret Key in client-side code, public repositories, or logs. The Secret Key should only be used in server-side applications.

Getting Your API Key

  1. Log in to the EasySlip Developer Portal
  2. Go to application settings
  3. Create a Partners API Key and Secret Key pair
  4. Note the Branch Key (UUID) from branch settings
  5. Store the Secret Key securely — it will only be shown once

Diagram

┌──────────────────────────────────────────────────────┐
│                    Application                        │
│                                                       │
│   ┌──────────────────────────────────────────────┐   │
│   │              Partners Client                      │   │
│   │   Partners API Key  ──→ X-API-Key (all requests) │   │
│   │   Secret Key   ──→ Signs all requests        │   │
│   └──────────────────────────────────────────────┘   │
│                                                       │
│   ┌───────────────┐  ┌───────────────┐               │
│   │   Branch A    │  │   Branch B    │  ...          │
│   │   Branch Key  │  │   Branch Key  │               │
│   │ ──→ X-Branch- │  │ ──→ X-Branch- │               │
│   │     Key       │  │     Key       │               │
│   └───────────────┘  └───────────────┘               │
└──────────────────────────────────────────────────────┘

Choosing Which Headers to Send

X-API-Key with the Partners API Key is always required. X-Branch-Key is only required for endpoints that need a specific branch:

Use CaseX-API-KeyX-Branch-KeyEndpoint
Verify slips / view dataPartners API Key (hex 64)Branch Key (UUID)/verify/bank, /verify/truewallet, /info
Manage specific branch / quota / bank accountsPartners API Key (hex 64)Branch Key (UUID)/b2b/branch, /b2b/branch/*
List branches / create branch / create bank accountPartners API Key (hex 64)Not required/b2b/branches, /b2b/bank-accounts

Required Headers

HeaderFormatDescription
X-API-KeystringPartners API Key (hex 64) — sent on every request
X-Branch-KeystringBranch Key (UUID) — sent when endpoint requires a branch
X-TimestampstringUnix timestamp in seconds
X-NoncestringUUID v4, unique per request
X-SignaturestringHMAC-SHA256 hex signature

Computing the Signature

Step 1: Build the String-to-Sign

{METHOD}\n{PATH}\n{TIMESTAMP}\n{NONCE}\n{BODY_SHA256}
ComponentDescription
METHODHTTP method in uppercase (GET, POST, PATCH, PUT, DELETE)
PATHRequest path including the leading / (e.g., /b2b/branches)
TIMESTAMPSame value as the X-Timestamp header
NONCESame value as the X-Nonce header
BODY_SHA256SHA-256 hex digest of the request body

Step 2: Compute the Body Hash

  • For requests with a body (POST, PUT, PATCH): compute the SHA-256 of the raw JSON body string
  • For requests without a body (GET, DELETE): compute the SHA-256 of an empty string ("")

TIP

The SHA-256 of an empty string is always: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Step 3: Sign with HMAC-SHA256

HMAC-SHA256(secretKey, stringToSign)

The result is a lowercase hex string.

Timestamp Tolerance

The server accepts timestamps within ±5 minutes of the current server time. Requests outside this window will be rejected with INVALID_TIMESTAMP.

Nonce Uniqueness

Each nonce must be a UUID v4 and must be unique. The server tracks recent nonces and will reject duplicates with DUPLICATE_NONCE. You must generate a new UUID for every request.

Code Examples

signRequest Function (works with any endpoint)

javascript
import crypto from 'crypto'

function signRequest({ method, path, body, apiKey, secretKey }) {
    const timestamp = Math.floor(Date.now() / 1000).toString()
    const nonce = crypto.randomUUID()
    const bodyString = body ? JSON.stringify(body) : ''
    const bodyHash = crypto.createHash('sha256').update(bodyString).digest('hex')

    const stringToSign = `${method}\n${path}\n${timestamp}\n${nonce}\n${bodyHash}`
    const signature = crypto.createHmac('sha256', secretKey).update(stringToSign).digest('hex')

    return {
        'X-API-Key': apiKey,
        'X-Timestamp': timestamp,
        'X-Nonce': nonce,
        'X-Signature': signature,
        ...(body ? { 'Content-Type': 'application/json' } : {}),
    }
}
python
import hashlib
import hmac
import json
import time
import uuid

def sign_request(method, path, body, api_key, secret_key):
    timestamp = str(int(time.time()))
    nonce = str(uuid.uuid4())
    body_string = json.dumps(body, separators=(',', ':')) if body else ''
    body_hash = hashlib.sha256(body_string.encode()).hexdigest()

    string_to_sign = f'{method}\n{path}\n{timestamp}\n{nonce}\n{body_hash}'
    signature = hmac.new(
        secret_key.encode(),
        string_to_sign.encode(),
        hashlib.sha256
    ).hexdigest()

    return {
        'X-API-Key': api_key,
        'X-Timestamp': timestamp,
        'X-Nonce': nonce,
        'X-Signature': signature,
    }
php
<?php

function signRequest(string $method, string $path, ?array $body, string $apiKey, string $secretKey): array
{
    $timestamp = (string) time();
    $nonce = sprintf(
        '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
        mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff),
        mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
        mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
    );
    $bodyString = $body ? json_encode($body, JSON_UNESCAPED_UNICODE) : '';
    $bodyHash = hash('sha256', $bodyString);

    $stringToSign = implode("\n", [$method, $path, $timestamp, $nonce, $bodyHash]);
    $signature = hash_hmac('sha256', $stringToSign, $secretKey);

    return [
        'X-API-Key' => $apiKey,
        'X-Timestamp' => $timestamp,
        'X-Nonce' => $nonce,
        'X-Signature' => $signature,
    ];
}
bash
#!/bin/bash
API_KEY="your_partners_api_key"
SECRET_KEY="your_secret_key"
METHOD="GET"
PATH_URL="/info"
TIMESTAMP=$(date +%s)
NONCE=$(uuidgen | tr '[:upper:]' '[:lower:]')

# For GET/DELETE, hash an empty string
BODY_HASH=$(printf '' | shasum -a 256 | cut -d' ' -f1)

STRING_TO_SIGN="${METHOD}\n${PATH_URL}\n${TIMESTAMP}\n${NONCE}\n${BODY_HASH}"
SIGNATURE=$(printf "${STRING_TO_SIGN}" | openssl dgst -sha256 -hmac "${SECRET_KEY}" | cut -d' ' -f2)

Example: Verify a Slip (requires X-Branch-Key)

javascript
const PARTNERS_API_KEY = 'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789'
const SECRET_KEY = 'your_secret_key'
const BRANCH_KEY = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'

const body = { payload: '00020101021230...' }
const headers = signRequest({
    method: 'POST',
    path: '/verify/bank',
    body,
    apiKey: PARTNERS_API_KEY,
    secretKey: SECRET_KEY,
})

const response = await fetch('https://api.easyslip.com/v2/verify/bank', {
    method: 'POST',
    headers: { ...headers, 'X-Branch-Key': BRANCH_KEY },
    body: JSON.stringify(body),
})
const result = await response.json()
console.log(result.data)
python
import requests

PARTNERS_API_KEY = 'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789'
SECRET_KEY = 'your_secret_key'
BRANCH_KEY = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'

body = {'payload': '00020101021230...'}
headers = sign_request('POST', '/verify/bank', body, PARTNERS_API_KEY, SECRET_KEY)
headers['X-Branch-Key'] = BRANCH_KEY
headers['Content-Type'] = 'application/json'

response = requests.post(
    'https://api.easyslip.com/v2/verify/bank',
    headers=headers,
    json=body,
)
result = response.json()
print(result['data'])
php
$partnersApiKey = 'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789';
$secretKey = 'your_secret_key';
$branchKey = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';

$body = ['payload' => '00020101021230...'];
$headers = signRequest('POST', '/verify/bank', $body, $partnersApiKey, $secretKey);
$headers['X-Branch-Key'] = $branchKey;

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => 'https://api.easyslip.com/v2/verify/bank',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => array_merge(
        array_map(fn($k, $v) => "$k: $v", array_keys($headers), array_values($headers)),
        ['Content-Type: application/json']
    ),
    CURLOPT_POSTFIELDS => json_encode($body),
]);
$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
print_r($result['data']);
bash
curl -X POST "https://api.easyslip.com/v2/verify/bank" \
  -H "X-API-Key: ${API_KEY}" \
  -H "X-Branch-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "X-Timestamp: ${TIMESTAMP}" \
  -H "X-Nonce: ${NONCE}" \
  -H "X-Signature: ${SIGNATURE}" \
  -H "Content-Type: application/json" \
  -d '{"payload": "00020101021230..."}'

Example: List Branches (no X-Branch-Key required)

javascript
const headers = signRequest({
    method: 'GET',
    path: '/b2b/branches',
    body: null,
    apiKey: PARTNERS_API_KEY,
    secretKey: SECRET_KEY,
})

const response = await fetch('https://api.easyslip.com/b2b/branches', { headers })
const result = await response.json()
console.log(result.data)
python
headers = sign_request('GET', '/b2b/branches', None, PARTNERS_API_KEY, SECRET_KEY)

response = requests.get('https://api.easyslip.com/b2b/branches', headers=headers)
result = response.json()
print(result['data'])
php
$headers = signRequest('GET', '/b2b/branches', null, $partnersApiKey, $secretKey);

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => 'https://api.easyslip.com/b2b/branches',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => array_map(fn($k, $v) => "$k: $v", array_keys($headers), array_values($headers)),
]);
$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
print_r($result['data']);
bash
curl -X GET "https://api.easyslip.com/b2b/branches" \
  -H "X-API-Key: ${API_KEY}" \
  -H "X-Timestamp: ${TIMESTAMP}" \
  -H "X-Nonce: ${NONCE}" \
  -H "X-Signature: ${SIGNATURE}"

Example: View Branch Quota (requires X-Branch-Key)

javascript
const headers = signRequest({
    method: 'GET',
    path: '/b2b/branch/quota',
    body: null,
    apiKey: PARTNERS_API_KEY,
    secretKey: SECRET_KEY,
})

const response = await fetch('https://api.easyslip.com/b2b/branch/quota', {
    headers: { ...headers, 'X-Branch-Key': BRANCH_KEY },
})
const result = await response.json()
console.log(result.data)
python
headers = sign_request('GET', '/b2b/branch/quota', None, PARTNERS_API_KEY, SECRET_KEY)
headers['X-Branch-Key'] = BRANCH_KEY

response = requests.get('https://api.easyslip.com/b2b/branch/quota', headers=headers)
result = response.json()
print(result['data'])
php
$headers = signRequest('GET', '/b2b/branch/quota', null, $partnersApiKey, $secretKey);
$headers['X-Branch-Key'] = $branchKey;

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => 'https://api.easyslip.com/b2b/branch/quota',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => array_map(fn($k, $v) => "$k: $v", array_keys($headers), array_values($headers)),
]);
$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
print_r($result['data']);
bash
curl -X GET "https://api.easyslip.com/b2b/branch/quota" \
  -H "X-API-Key: ${API_KEY}" \
  -H "X-Branch-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "X-Timestamp: ${TIMESTAMP}" \
  -H "X-Nonce: ${NONCE}" \
  -H "X-Signature: ${SIGNATURE}"

Permissions

Each Partners API key has a set of permissions that determine what actions it can perform:

PermissionDescription
branch:readView branch list and details
branch:writeCreate, edit, and delete branches
quota:readView quota information
quota:writeSet, adjust, and reset quotas
bank-account:readView bank accounts linked to branches
bank-account:writeCreate, link, and unlink bank accounts

WARNING

If your API key does not have the required permission for an endpoint, the request will be rejected with 403 Forbidden.

Security Best Practices

1. Never Expose Keys in Client-Side Code

javascript
// ❌ BAD - Don't do this
const API_KEY = 'your-api-key-here';

// ✅ GOOD - Use environment variables
const API_KEY = process.env.EASYSLIP_API_KEY;

2. Use Environment Variables

bash
EASYSLIP_PARTNERS_API_KEY=your-partners-api-key
EASYSLIP_SECRET_KEY=your-secret-key
EASYSLIP_BRANCH_KEY=your-branch-uuid
javascript
import 'dotenv/config';

const partnersApiKey = process.env.EASYSLIP_PARTNERS_API_KEY;
const secretKey = process.env.EASYSLIP_SECRET_KEY;
const branchKey = process.env.EASYSLIP_BRANCH_KEY;
php
$partnersApiKey = getenv('EASYSLIP_PARTNERS_API_KEY');
$secretKey = getenv('EASYSLIP_SECRET_KEY');
$branchKey = getenv('EASYSLIP_BRANCH_KEY');
python
import os

partners_api_key = os.environ.get('EASYSLIP_PARTNERS_API_KEY')
secret_key = os.environ.get('EASYSLIP_SECRET_KEY')
branch_key = os.environ.get('EASYSLIP_BRANCH_KEY')

3. Make Requests from Server Only

javascript
// ❌ BAD - Client-side request exposes API key and secret
fetch('https://api.easyslip.com/v2/verify/bank', {
  headers: { 'X-API-Key': 'your-key', 'X-Signature': '...' }
});

// ✅ GOOD - Make request through your own server
fetch('/api/verify-slip', {
  method: 'POST',
  body: JSON.stringify({ image: slipImage })
});

4. IP Whitelisting

Configure IP whitelisting to restrict API access to specific IP addresses:

  1. Go to Application Settings
  2. Add your server's IP addresses to the whitelist
  3. Use * to allow all IPs (not recommended for production)

Authentication Errors

Error CodeHTTP StatusDescriptionSolution
INVALID_AUTH_HEADERS401Headers are invalid or incompleteEnsure X-API-Key, X-Timestamp, X-Nonce, X-Signature are all present
INVALID_API_KEY401Invalid API KeyVerify your Partners API Key
MISSING_BRANCH_KEY401X-Branch-Key not providedAdd X-Branch-Key header for endpoints that require a branch
INVALID_BRANCH_KEY401Invalid Branch KeyVerify your Branch Key (UUID)
INVALID_TIMESTAMP401Timestamp outside the ±5 minute windowCheck your system clock
DUPLICATE_NONCE401Duplicate nonceGenerate a new UUID for every request
INVALID_SIGNATURE401Signature mismatchVerify your Secret Key and signature computation
SERVICE_SUSPENDED403Application has been suspendedContact support
BRANCH_INACTIVE403Branch has been deactivatedReactivate the branch
IP_NOT_ALLOWED403IP not in whitelistAdd the IP to the whitelist
BRANCH_QUOTA_EXCEEDED403Branch quota exceededUpgrade your plan or wait for quota reset
PERMISSION_DENIED403Access deniedCheck the Partners Client permissions

Multi-Branch Support

EasySlip supports multiple branches per application. Each branch has its own Branch Key:

  • Main Branch: Primary Branch Key with full quota
  • Sub-Branches: Additional Branch Keys with separate quota tracking

This allows you to:

  • Track usage per integration point
  • Apply different IP restrictions per branch
  • Manage quota allocation across teams

Next Steps

Bank Slip Verification API for Thai Banking