Skip to content

Webhooks ​

Not available yet — do not build against this page

Outbound partner webhooks are not implemented. RekberPay does not currently POST to a partner URL, there is no place to register one, and no escrow.released or payment.completed delivery exists.

Everything below the "Planned contract" heading is a design document for a future release, retained so the contract is stable when it ships. It is not a description of current behaviour.

Today, use polling — see What to use instead directly below.

Earlier revisions of this page described the delivery, signature, and retry behaviour as if it were live. It was not, and that error is corrected here rather than quietly deleted: if you built a verification path against it, nothing was ever going to arrive at your endpoint.

What to use instead ​

Two endpoints cover the state changes webhooks would have pushed:

MethodPathPurpose
GET/api/escrows/:id/payment-statusPoll an escrow's payment state
GET/api/jasabayar/:id/payment-statusPoll a Jasa Pembayaran order's payment state

GET /api/escrows/:id and GET /api/jasabayar/:id return the full current state including status and timestamps, which is what you would reconcile against a webhook anyway.

A reasonable poll cadence is every 5–10 seconds while a checkout is open, and backing off to once a minute afterwards. Because both endpoints are GET, they cost you nothing in idempotency bookkeeping.

If outbound webhooks would materially change your integration, say so at [email protected] — demand is what prioritises the work.


Planned contract ​

WARNING

Everything from here on is unimplemented. Do not write verification code against it yet.

RekberPay POSTs JSON to your configured webhook URL when escrow state changes. This page documents every event, the signature scheme, and the retry policy.

For the integration walkthrough (verify, handle retries, idempotency), see Handling webhooks.

Delivery ​

  • Method: POST
  • Content-Type: application/json
  • From IPs: Documented at https://api.rekberpay.com/.well-known/webhook-ips (allowlist these in your firewall)
  • Timeout: 5 seconds — return 2xx within that window
  • Retries: Up to 7 attempts on non-2xx (see retry table)

Signature ​

Every webhook includes a signature field — HMAC-SHA256 of the canonical event string, hex-encoded.

Canonical string:

event + escrow_id + amount + status + timestamp

Verify with:

js
import crypto from 'node:crypto';

function verify(body, secret) {
    const canonical = `${body.event}${body.escrow_id}${body.amount}${body.status}${body.timestamp}`;
    const expected = crypto.createHmac('sha256', secret).update(canonical).digest('hex');
    return crypto.timingSafeEqual(
        Buffer.from(body.signature, 'hex'),
        Buffer.from(expected, 'hex'),
    );
}

See Authentication for the HMAC scheme. Same algorithm as link signing, different secret.

Common envelope ​

Every webhook has these fields:

FieldTypeDescription
eventstringEvent name (see below)
escrow_idUUIDThe escrow this event is about
refstringYour ref from when you signed the link
amountintegerIDR, no decimals
statusstringThe escrow's state after this event
timestampISO 8601When the event occurred (UTC)
dataobjectEvent-specific extra fields
signaturehex stringHMAC-SHA256 verification signature

Events ​

escrow.created ​

Fired immediately after a partner-signed escrow is created.

FieldTypeDescription
data.expires_atISO 8601When the escrow auto-cancels if unpaid
data.escrow_urlURLBuyer payment link
json
{
    "event": "escrow.created",
    "escrow_id": "7f8a9b0c-1d2e-3f4a-5b6c-7d8e9f0a1b2c",
    "ref": "MARKETPLACE_98765",
    "amount": 22500000,
    "status": "pending_payment",
    "timestamp": "2026-05-18T07:00:00.000Z",
    "data": {
        "expires_at": "2026-05-25T07:00:00.000Z",
        "escrow_url": "https://rekberpay.com/escrow/7f8a.../pay"
    },
    "signature": "..."
}

payment.completed ​

Buyer paid, funds in escrow.

FieldTypeDescription
data.payment_methodstringqris, bank_transfer, gopay, shopeepay, crypto
data.paid_atISO 8601When the gateway confirmed
data.feeintegerPlatform fee withheld on release
json
{
    "event": "payment.completed",
    "escrow_id": "7f8a...",
    "ref": "MARKETPLACE_98765",
    "amount": 22500000,
    "status": "paid",
    "timestamp": "2026-05-18T09:27:00.000Z",
    "data": {
        "payment_method": "qris",
        "paid_at": "2026-05-18T09:26:55.000Z",
        "fee": 5000
    },
    "signature": "..."
}

escrow.sent ​

Seller marked the goods/service delivered.

FieldTypeDescription
data.tracking_numberstring | nullIf the seller provided one
data.courierstring | nullCourier name (e.g., JNE, J&T)
data.delivered_atISO 8601Server-side timestamp

escrow.received ​

Buyer confirmed receipt.

json
{
    "event": "escrow.received",
    "escrow_id": "7f8a...",
    "ref": "MARKETPLACE_98765",
    "amount": 22500000,
    "status": "received",
    "timestamp": "2026-05-19T14:00:00.000Z",
    "data": {
        "confirmed_at": "2026-05-19T14:00:00.000Z"
    },
    "signature": "..."
}

escrow.released ​

Funds released to seller balance. Terminal state.

FieldTypeDescription
data.released_bystringbuyer, auto_release, or admin
data.release_methodstringmanual or sla_timeout
data.seller_payoutintegerAmount credited to seller's RekberPay balance

escrow.refunded ​

Funds refunded to buyer. Terminal state.

FieldTypeDescription
data.refunded_bystringadmin or auto_refund
data.refund_reasonstringFree-form note

escrow.disputed ​

Buyer or seller opened a dispute.

FieldTypeDescription
data.opened_bystringbuyer or seller
data.reasonstringnot_as_described, not_received, damaged, fraud, other
data.descriptionstringFree-form text from the disputer

dispute.resolved ​

Admin resolved a dispute.

FieldTypeDescription
data.outcomestringreleased, refunded, or hold
data.resolution_notestringAdmin's note

status reflects the new escrow state (released, refunded, or hold).

What status looks like ​

The status field is the escrow's state after the event:

EventResulting status
escrow.createdpending_payment
payment.completedpaid
escrow.sentsent
escrow.receivedreceived
escrow.releasedreleased
escrow.refundedrefunded
escrow.disputeddisputed
dispute.resolvedreleased, refunded, or hold

Subscribing to events ​

By default, you receive all events. To filter, email [email protected] with the events you want.

We don't currently support per-escrow event subscriptions — partner-level only.

Replaying ​

Failed deliveries can be replayed from the partner dashboard at https://dashboard.rekberpay.com/webhooks/<event-id>/replay. Replays carry the same event_id so your idempotency key works.

See also ​

Released under the proprietary RekberPay license. Built for Indonesian merchants.