Batch Async Verify
Submit multiple bank slips in a single request. Each slip is queued as its own async job with its own jobId; results are delivered per-job to your webhook and can be polled individually. Up to 100 slips per request.
Bank only, payload only
Like single async verify, batch is bank-only and each slip is a QR payload string.
Endpoint
POST /verify/bank/batchFull URL: https://api-partners.easyslip.com/v2/verify/bank/batch
Authentication
Required. Uses HMAC-SHA256 with your branch UUID as X-API-Key. See Authentication Guide.
X-API-Key: YOUR_BRANCH_UUID
X-Timestamp: 1700000000
X-Nonce: 550e8400-e29b-41d4-a716-446655440000
X-Signature: HMAC_SHA256_SIGNATURE
Content-Type: application/jsonRequest
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
slips | array | Yes | 1-100 slip objects (see below) |
callbackUrl | string | Conditional | HTTPS URL results are POSTed to (one per job). Required unless a default webhook URL is configured for the branch. Max 255 chars. |
Each object in slips accepts the same per-slip fields as single async verify:
| Field | Type | Required | Description |
|---|---|---|---|
payload | string | Yes | QR code payload (1-128 characters) |
remark | string | No | Custom remark (1-255 characters) |
matchAccount | boolean | No | Match receiver with registered accounts |
matchAmount | number | No | Expected amount to validate |
checkDuplicate | boolean | No | Check for duplicate slip (default: false) |
All-or-nothing validation
If any slip is invalid (e.g. missing payload), the whole request is rejected with 400 and nothing is queued. callbackUrl applies to every job in the batch and has the same HTTPS + public-address requirements as single async verify.
Request Body
{
"callbackUrl": "https://example.com/webhooks/easyslip",
"slips": [
{ "payload": "0000000000000000000000000000000000000000000000A", "remark": "Order #1" },
{ "payload": "0000000000000000000000000000000000000000000000B", "remark": "Order #2", "checkDuplicate": true }
]
}Type Definitions
// Request
interface BatchAsyncVerifyRequest {
callbackUrl?: string; // https URL, max 255 chars (required if no branch webhook)
slips: Array<{
payload: string; // 1-128 chars
remark?: string;
matchAccount?: boolean;
matchAmount?: number;
checkDuplicate?: boolean;
}>; // 1-100 items
}
// Response
interface BatchAsyncVerifyResponse {
success: true;
data: {
batchId: string; // UUID grouping this batch
jobs: Array<{ jobId: string; index: number }>; // one per submitted slip, in order
};
message: string;
}Examples
curl -X POST https://api-partners.easyslip.com/v2/verify/bank/batch \
-H "X-API-Key: YOUR_BRANCH_UUID" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "X-Nonce: ${NONCE}" \
-H "X-Signature: ${SIGNATURE}" \
-H "Content-Type: application/json" \
-d '{
"callbackUrl": "https://example.com/webhooks/easyslip",
"slips": [
{ "payload": "0000000000000000000000000000000000000000000000A" },
{ "payload": "0000000000000000000000000000000000000000000000B" }
]
}'const verifyBankBatch = async (slips, callbackUrl) => {
const body = JSON.stringify({ callbackUrl, slips });
const response = await fetch('https://api-partners.easyslip.com/v2/verify/bank/batch', {
method: 'POST',
headers: {
...signRequest({ method: 'POST', path: '/verify/bank/batch', body, apiKey: 'YOUR_BRANCH_UUID', secretKey: 'YOUR_SECRET_KEY' }),
'Content-Type': 'application/json'
},
body
});
const result = await response.json();
if (!result.success) throw new Error(result.error.message);
return result.data; // { batchId, jobs: [{ jobId, index }] }
};
// Usage
const { batchId, jobs } = await verifyBankBatch(
[{ payload: '0000000000000000000000000000000000000000000000A' }],
'https://example.com/webhooks/easyslip'
);
console.log(batchId, jobs);interface Slip { payload: string; remark?: string; matchAccount?: boolean; matchAmount?: number; checkDuplicate?: boolean; }
async function verifyBankBatch(slips: Slip[], callbackUrl: string) {
const body = JSON.stringify({ callbackUrl, slips });
const response = await fetch('https://api-partners.easyslip.com/v2/verify/bank/batch', {
method: 'POST',
headers: {
...signRequest({ method: 'POST', path: '/verify/bank/batch', body, apiKey: process.env.EASYSLIP_API_KEY!, secretKey: process.env.EASYSLIP_SECRET_KEY! }),
'Content-Type': 'application/json'
},
body
});
const result = await response.json();
if (!result.success) throw new Error(result.error?.message || 'Batch verify failed');
return result.data as { batchId: string; jobs: { jobId: string; index: number }[] };
}function verifyBankBatch(array $slips, string $callbackUrl): array
{
$data = ['callbackUrl' => $callbackUrl, 'slips' => $slips];
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api-partners.easyslip.com/v2/verify/bank/batch',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
// Use signRequest() helper - see Authentication Guide
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode($data)
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
if (!$result['success']) {
throw new Exception($result['error']['message']);
}
return $result['data'];
}import requests
import os
def verify_bank_batch(slips: list, callback_url: str) -> dict:
data = {'callbackUrl': callback_url, 'slips': slips}
response = requests.post(
'https://api-partners.easyslip.com/v2/verify/bank/batch',
headers={
**sign_request('POST', '/verify/bank/batch', data, os.environ['EASYSLIP_API_KEY'], os.environ['EASYSLIP_SECRET_KEY']),
'Content-Type': 'application/json'
},
json=data
)
result = response.json()
if not result['success']:
raise Exception(result['error']['message'])
return result['data']Response
Accepted (202)
{
"success": true,
"data": {
"batchId": "7b2e1f10-1c2d-4e3f-9a8b-0c1d2e3f4a5b",
"jobs": [
{ "jobId": "550e8400-e29b-41d4-a716-446655440000", "index": 0 },
{ "jobId": "660f9511-f3ab-52e5-b827-557766551111", "index": 1 }
]
},
"message": "Batch queued"
}index matches the position of each slip in the request slips array. Each jobId behaves exactly like a single async job — it produces its own webhook and is pollable at GET /verify/bank/jobs/:jobId.
Error Responses
Invalid slip in batch (400)
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Payload is required and cannot be empty"
}
}Too many slips (400)
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "slips must not exceed 100 items"
}
}Notes
- Maximum 100 slips per request; split larger workloads into multiple batches.
- Validation is all-or-nothing — one bad slip rejects the whole request with nothing queued.
- Each slip becomes an independent job (its own webhook + pollable status); the shared
batchIdalso appears in each job's webhook payload.