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-partners.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-partners.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-partners.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-partners.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-partners.easyslip.com/v2/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-partners.easyslip.com/v2/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-partners.easyslip.com/v2/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-partners.easyslip.com/v2/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-partners.easyslip.com/v2/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-partners.easyslip.com/v2/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-partners.easyslip.com/v2/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-partners.easyslip.com/v2/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-partners.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