Skip to content

Escrows API

The escrow endpoints are the core of any partner integration. You'll create escrows via signed links, redirect buyers to the hosted checkout, and poll or webhook your way to confirmation.

Endpoints

MethodPathAuthIdempotent
POST/api/escrows/from-linkHMAC signatureYes
GET/api/escrows/:id/previewnone (public)
GET/api/escrows/:id/payment-statusnone (public)

POST/api/escrows/from-link

Create an escrow from a signed partner link.

Authentication: Session cookie (for the buyer caller) + HMAC signature in the sig field inside the URL. Idempotency: supported via Idempotency-Key header

Request

http
POST /api/escrows/from-link
Content-Type: application/json
Cookie: rekberpay_session=...
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

Body

FieldTypeRequiredDescription
linkURLYesThe fully constructed and signed partner URL (e.g. https://rekberpay.com/...?seller=...&sig=...)
amountintegerYesPrice in IDR (10,000 – 500,000,000)

The backend extracts and verifies the seller, item, category, ref, v, sig, expires, and desc directly from the query parameters of the provided link. Ensure the signed link has not expired and the signature is valid.

Example

bash
curl -X POST https://api.rekberpay.com/api/escrows/from-link \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Cookie: rekberpay_session=..." \
  -d '{
    "link": "https://rekberpay.com/?seller=0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d&item=iPhone%2015%20Pro%20256GB&category=goods&amount=22500000&expires=1748448000&ref=MARKETPLACE_98765&v=1&sig=f3a9c1e8b7d6a5f4c3b2a1e0d9c8b7a6f5e4d3c2b1a0f9e8d7c6b5a4f3e2d1c0",
    "amount": 22500000
  }'

Response

201 Created

json
{
    "success": true,
    "data": {
        "id": "7f8a9b0c-1d2e-3f4a-5b6c-7d8e9f0a1b2c",
        "buyer_id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
        "seller_id": "0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
        "item_name": "iPhone 15 Pro 256GB",
        "amount": "22500000",
        "description": "",
        "category": "goods",
        "status": "pending_payment",
        "partner_ref": "MARKETPLACE_98765",
        "partner_sig_version": 1,
        "created_at": "2026-05-18T07:00:00.000Z",
        "updated_at": "2026-05-18T07:00:00.000Z"
    }
}
FieldTypeDescription
idUUIDEscrow ID — store this against your order
buyer_idUUIDThe authenticated user who made the request
seller_idUUIDThe seller extracted from the link
statusstringAlways pending_payment on creation
amountstringEcho of the amount you sent
partner_refstringEcho of your ref from the link

Errors

StatusCodeCause
400INVALID_BODYRequired field missing or wrong type
400INVALID_URLMalformed URL provided in link
422HOST_NOT_ALLOWEDLink host is not permitted
422UNSUPPORTED_SIG_VERSIONSignature version is not supported
422SIG_INCOMPLETESigned links require ref, expires, and v
422INVALID_EXPIRESexpires must be a unix timestamp
422LINK_EXPIREDPartner link has expired
422EXPIRES_TOO_FARexpires cannot be more than 30 days in the future
422SIG_MISMATCHSignature verification failed
422INVALID_SELLERseller must be a UUID v4
422SELLER_IS_BUYERCannot create escrow with yourself as seller
422SELLER_NOT_FOUNDSeller user does not exist
422INVALID_CATEGORYcategory must be goods, service, or digital
422INVALID_ITEMitem is required in the link

GET/api/escrows/:id/preview

Get public escrow details. No authentication required — useful for showing order details on your confirmation page before redirecting to payment.

Request

http
GET /api/escrows/7f8a9b0c-1d2e-3f4a-5b6c-7d8e9f0a1b2c/preview

Response

200 OK

json
{
    "success": true,
    "data": {
        "id": "7f8a9b0c-1d2e-3f4a-5b6c-7d8e9f0a1b2c",
        "item_name": "iPhone 15 Pro 256GB",
        "description": "Brand new sealed",
        "amount": 22500000,
        "category": "goods",
        "status": "pending_payment",
        "seller": {
            "id": "0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
            "display_name": "Toko ABC",
            "kyc_verified": true
        },
        "expires_at": "2026-05-25T07:00:00.000Z",
        "created_at": "2026-05-18T07:00:00.000Z"
    }
}

This endpoint exposes only public-safe fields. Buyer details, internal IDs, and audit history are never returned.

Errors

StatusCodeCause
404NOT_FOUNDEscrow doesn't exist
410EXPIREDEscrow expired and was cancelled

GET/api/escrows/:id/payment-status

Poll payment status for a specific escrow. Authentication: session cookie (buyer, seller, or admin only).

Request

http
GET /api/escrows/7f8a9b0c-1d2e-3f4a-5b6c-7d8e9f0a1b2c/payment-status
Cookie: rekberpay_session=...

Response

200 OK

json
{
    "success": true,
    "data": {
        "id": "pay_8f9a0b1c2d3e4f5a6b7c8d9e",
        "escrow_id": "7f8a9b0c-1d2e-3f4a-5b6c-7d8e9f0a1b2c",
        "provider": "paymento",
        "method": "crypto",
        "gateway_order_id": "RBP-7f8a9b0c-1781234567890-a1b2",
        "amount": "22612500",
        "status": "pending",
        "qr_url": null,
        "va_number": null,
        "redirect_url": "https://app.paymento.io/gateway?token=...",
        "raw_response": "{}",
        "expires_at": null,
        "created_at": "2026-05-18T09:26:55.000Z",
        "updated_at": "2026-05-18T09:26:55.000Z"
    }
}
FieldTypeDescription
idUUIDInternal payment link ID
providerstringInternal gateway provider name
methodstringPayment method (e.g. qris, bank_transfer, crypto)
gateway_order_idstringThe order ID sent to the gateway
amountstringTotal amount to be paid (base escrow amount + gateway surcharge)
statusstringOne of pending, paid, expired, failed, awaiting_confirmation
qr_urlURL | nullQRIS Code image URL (if applicable)
va_numberstring | nullVirtual Account number (if applicable)

The raw_response object may contain internal provider metadata. It is highly advised not to rely on raw_response as it varies drastically across gateways.

Polling guidance

  • Poll every 5 seconds while status is pending
  • Stop polling once status is paid, failed, or expired
  • Cap total polling at 2 minutes then show a "payment pending" message
  • Don't poll faster than 1 req/sec — you'll get rate-limited

Errors

StatusCodeCause
401UNAUTHORIZEDUser is not logged in
403FORBIDDENUser is not a party in this escrow
404ESCROW_NOT_FOUNDEscrow doesn't exist
404NO_PAYMENTNo payment attempt found for this escrow
429RATE_LIMITEDPolling too fast

GET/api/escrows/:id/tracking

Get shipping tracking for a physical-goods escrow: the persisted courier + AWB, a best-effort live journey snapshot, the official-courier deep link, and the courier list used by the seller's "mark shipped" form.

Authentication: session cookie (buyer, seller, or admin only).

The live snapshot is best-effort enrichment. The courier + AWB are always persisted and track_url always resolves, so a slow or unavailable upstream returns snapshot.found: false rather than failing the request. The snapshot is cached for ~10 minutes server-side.

Response

200 OK

json
{
    "success": true,
    "data": {
        "tracking": {
            "courier": "anteraja",
            "courier_name": "AnterAja",
            "number": "TSA-80531250806",
            "track_url": "https://anteraja.id/tracking?awb=TSA-80531250806"
        },
        "snapshot": {
            "found": true,
            "courier": "anteraja",
            "awb": "TSA-80531250806",
            "status": "Delivered",
            "recipient": "Diterima oleh Rizaldy (Penerima Asli)",
            "events": [
                { "date": "25 Jun 2026 15:19", "description": "Delivery sukses, paket telah diterima." },
                { "date": "25 Jun 2026 10:31", "description": "Paket sudah di-pickup oleh kurir." }
            ],
            "checkedAt": "2026-06-27T05:40:00.000Z"
        },
        "couriers": [
            { "slug": "jne", "name": "JNE" },
            { "slug": "anteraja", "name": "AnterAja" }
        ]
    }
}
FieldTypeDescription
trackingobject | nullPersisted courier/AWB + deep link; null until a resi is attached
snapshotobject | nullLive result; snapshot.found is false when upstream is unavailable
snapshot.eventsarrayJourney legs, newest first; empty when not parseable
couriersarray{ slug, name } options for the seller's courier dropdown

Errors

StatusCodeCause
403FORBIDDENUser is not a party in this escrow
404NOT_FOUNDEscrow doesn't exist

POST/api/escrows/:id/tracking/refresh

Force a fresh upstream tracking check, bypassing the server cache.

Authentication: session cookie (buyer, seller, or admin only). Rate limit: one call per escrow every 5 minutes (couriers update scans on the order of hours, so a tighter window just hammers the upstream for no fresher data).

Response

200 OK

Same shape as GET /api/escrows/:id/tracking, with a freshly fetched snapshot.

Errors

StatusCodeCause
400NO_TRACKINGEscrow has no courier/AWB attached yet
403FORBIDDENUser is not a party in this escrow
404NOT_FOUNDEscrow doesn't exist
429RATE_LIMITEDRefreshed within the last 5 minutes

To attach tracking, the seller includes an optional { "courier": "<slug>", "tracking_number": "<awb>" } body when calling POST /api/escrows/:id/send to mark the item shipped. Both fields must be present together; omitting them ships without tracking (e.g. digital goods).

See also

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