Skip to main content

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.

Two Services, Two Auth Header Sets
  • Lookup endpoints (/api/psp/v1/merchants/*) use X-API-Key-ID / X-Timestamp / X-Signature
  • Events endpoints (/psp/v1/events, /psp/v1/settlements) use X-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:

ParamTypeDefaultDescription
limitint100Maximum 500
cursorstringOpaque pagination cursor from the previous response
updated_sinceISO8601Delta sync — only merchants with updated_at ≥ this value
category_codestringFilter by MCC category (e.g., 5411)
include_outletsboolfalseInclude 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, default false)
  • include_owners (bool, default false)

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):

ParamTypeDescription
phonestringExact match
emailstringExact match, case-insensitive
npwpstringExact match
nmidstringExact 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:

EndpointNote
PATCH /api/psp/v1/merchants/{id}/nmid-assignmentTier-1 Internal PSP only