Endpoint List
All endpoints are available under the base URL:
Production : https://api-merchant.kesles.com
Staging : https://api-merchant-staging.kesles.com
Auth is required on every endpoint — see Integration Contract.
- Lookup endpoints (
/api/psp/v1/merchants/*) useX-API-Key-ID/X-Timestamp/X-Signature - Events endpoints (
/psp/v1/events,/psp/v1/settlements) useX-PSP-Key-ID/X-PSP-Timestamp/X-PSP-Signature
Merchant Lookup Endpoints
GET /api/psp/v1/merchants
List all active merchants belonging to your bank. Cursor paginated.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
limit | int | 100 | Maximum 500 |
cursor | string | — | Opaque pagination cursor from the previous response |
updated_since | ISO8601 | — | Delta sync — only merchants with updated_at ≥ this value |
category_code | string | — | Filter by MCC category (e.g., 5411) |
include_outlets | bool | false | Include outlet list per merchant |
Response 200:
{
"items": [
{
"id": "b8f3a2c1-uuid",
"merchant_name": "Warung Pak Budi",
"legal_name": "CV Sumber Rejeki",
"nmid": "ID102326873XXXX",
"bank_code": "BMRI",
"category_code": "5411",
"category_name": "Grocery Stores",
"phone": "081234567890",
"email": "budi@example.com",
"npwp": "01.234.567.8-123.000",
"address": {
"line": "Jl. Sudirman No. 123",
"city": "Makassar",
"province": "Sulawesi Selatan",
"postal_code": "90111",
"country": "ID"
},
"status": "active",
"kyc_status": "verified",
"kyc_verified_at": "2026-05-01T09:30:00Z",
"created_at": "2026-05-01T08:00:00Z",
"updated_at": "2026-05-23T14:23:45Z"
}
],
"pagination": {
"next_cursor": "eyJpZCI6ImJmYTMifQ==",
"has_more": true
},
"meta": {
"returned_count": 100,
"active_only": true
}
}
Rate limit: 60 req/min · Performance: p95 < 300ms
GET /api/psp/v1/merchants/{id}
Merchant detail by internal UUID.
Path param: id — merchant UUID
Query params:
include_outlets(bool, defaultfalse)include_owners(bool, defaultfalse)
Response 200:
{
"merchant": {
"id": "b8f3a2c1-uuid",
"merchant_name": "Warung Pak Budi",
"nmid": "ID102326873XXXX",
"bank_code": "BMRI",
"status": "active",
"kyc_status": "verified",
"device_model": "QRIS Plus Q161 Pro",
"device_status": "active",
"...": "other fields same as the /merchants list"
},
"outlets": [],
"owners": []
}
Rate limit: 600 req/min · Performance: p95 < 100ms (Redis cache 30s)
GET /api/psp/v1/merchants/by-nmid/{nmid}
Reverse lookup: find merchant by NMID. The most frequently called endpoint — used when a transaction comes in with an NMID.
Path param: nmid — NMID string (e.g., ID102326873XXXX)
Response 200: Same as GET /merchants/{id}
Rate limit: 1,000 req/min · Performance: p95 < 50ms (indexed + Redis cache 30s)
GET /api/psp/v1/merchants/search
Search for merchants for duplicate detection or dispute resolution.
Query params (at least 1 required):
| Param | Type | Description |
|---|---|---|
phone | string | Exact match |
email | string | Exact match, case-insensitive |
npwp | string | Exact match |
nmid | string | Exact match |
Response 200:
{
"items": [ /* same as the /merchants list */ ],
"meta": { "returned_count": 1 }
}
Rate limit: 120 req/min
Payment Event Endpoints
POST /psp/v1/events
Forward a payment event from PSP to Kesles. Uses flat payload — not a nested envelope.
Auth headers: X-PSP-Key-ID / X-PSP-Timestamp / X-PSP-Signature (see Integration Contract)
event_type values: transaction.success, transaction.failed, transaction.cancelled, transaction.refunded, transaction.reversed, qris.payment.success, qris.payment.failed, qris.payment.refunded, nmid.status_changed
Example — transaction.success:
{
"external_event_id": "psp-evt-abc-123",
"event_type": "transaction.success",
"nmid": "ID102326873XXXX",
"gross_amount": 75000,
"transaction_code": "TRX-PSP-20260523-001",
"reference_number": "BI20260523142345XXXX",
"transaction_at": "2026-05-23T14:23:45Z",
"status": "success"
}
Response 200:
{
"status": "success",
"flow": "A",
"event_id": "3b1e...",
"merchant_id": "8f3c..."
}
The event_id in the response is the internal event-log UUID (psp.event_log.id), not the external_event_id you sent. A duplicate external_event_id returns HTTP 200 with {"status":"duplicate_skipped"} instead.
Rate limit: 6,000 req/min
POST /psp/v1/settlements
Forward a daily settlement batch.
Auth headers: X-PSP-Key-ID / X-PSP-Timestamp / X-PSP-Signature
Request body:
{
"external_event_id": "psp-sett-20260524-001",
"settlement_id": "psp-sett-uuid",
"nmid": "ID102326873XXXX",
"bank_code": "BMRI",
"gross_amount": 3240000,
"mdr_total": 22680,
"net_amount": 3217320,
"settlement_date": "2026-05-24",
"completed_at": "2026-05-24T08:00:00Z"
}
Rate limit: 600 req/min
Restricted Endpoints
The following endpoints return 403 for external bank PSPs:
| Endpoint | Note |
|---|---|
PATCH /api/psp/v1/merchants/{id}/nmid-assignment | Tier-1 Internal PSP only |