Skip to content

Disputes API ​

A dispute freezes the escrow while RekberPay looks at the case. Either party can open one while the escrow is paid, sent or received. Both parties and RekberPay staff talk in the dispute thread; RekberPay then releases the money to the seller, refunds the buyer or holds it. Resolution is a staff action, so there is no endpoint for it here.

The Handling disputes guide shows the whole flow.

Endpoints ​

MethodPathEndpointAuth
POST/v1/escrows/:id/disputeOpen a disputeKey or session
GET/v1/disputesList your disputesKey or session
GET/v1/disputes/:idGet a disputeKey or session
GET/v1/disputes/:id/messagesList dispute messagesKey or session
POST/v1/disputes/:id/messagesSend a dispute messageKey or session

Open a dispute ​

POST/v1/escrows/:id/dispute

Opens a dispute and moves the escrow to disputed. The other party and RekberPay are notified.

  • Auth: A personal API key (Authorization: Bearer <key>) or a session cookie with X-CSRF-Token on writes.
  • Who can call: The buyer or the seller, while the escrow is paid, sent or received.
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not supported. A second dispute on the same escrow answers 409 NOT_DISPUTABLE, so a retry cannot open two.

Path parameters ​

NameTypeDescription
idUUIDThe escrow ID

Body (JSON) ​

NameTypeRequiredDescription
reason_categorystringYesitem_not_received, item_not_as_described, seller_unresponsive or other
descriptionstringYesWhat happened, 1 to 3,000 characters

Example request ​

bash
curl -X POST https://api.rekberpay.com/v1/escrows/55555551-5555-4555-8555-555555555555/dispute \
  -H "Authorization: Bearer $REKBERPAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reason_category": "item_not_as_described",
    "description": "Logo yang dikirim tidak sesuai brief: warna dan font berbeda dari yang disepakati."
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/escrows/55555551-5555-4555-8555-555555555555/dispute', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_API_KEY}`,
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        reason_category: "item_not_as_described",
        description: "Logo yang dikirim tidak sesuai brief: warna dan font berbeda dari yang disepakati."
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/escrows/55555551-5555-4555-8555-555555555555/dispute",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_API_KEY']}",
    },
    json={
        "reason_category": "item_not_as_described",
        "description": "Logo yang dikirim tidak sesuai brief: warna dan font berbeda dari yang disepakati.",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/escrows/55555551-5555-4555-8555-555555555555/dispute');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'reason_category' => 'item_not_as_described',
        'description' => 'Logo yang dikirim tidak sesuai brief: warna dan font berbeda dari yang disepakati.',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

201 Created

json
{
    "success": true,
    "data": {
        "id": "77777771-7777-4777-8777-777777777777",
        "escrow_id": "55555551-5555-4555-8555-555555555555",
        "initiator_id": "11111110-1111-4111-8111-111111111111",
        "reason_category": "item_not_as_described",
        "description": "Logo yang dikirim tidak sesuai brief: warna dan font berbeda dari yang disepakati.",
        "status": "open",
        "priority": "normal",
        "resolution_note": null,
        "resolved_by": null,
        "resolved_at": null,
        "created_at": "2026-10-13T14:23:18.823Z",
        "updated_at": "2026-10-13T14:23:18.823Z"
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYA field failed validation
403FORBIDDENOnly the buyer or the seller can open a dispute
404NOT_FOUNDNo escrow with this ID
409NOT_DISPUTABLEThe escrow is not paid, sent or received
409DISPUTE_EXISTSAn open dispute already exists
409ESCROW_MOVEDThe escrow changed status while the call ran
409ACCOUNT_NOT_ACTIVEOne of the parties is no longer active
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or API key, the session expired, or the key is wrong or revoked
401WORKSPACE_KEY_ONLYA workspace key (rpe_...) was sent. Those only work under /v1/enterprise
403CSRF_FAILEDSession call without the matching X-CSRF-Token header
403ACCOUNT_INACTIVE, SUSPENDEDThe account is closed, being deleted or suspended
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

A real NOT_DISPUTABLE answer:

409 Conflict

json
{
    "success": false,
    "error": {
        "code": "NOT_DISPUTABLE",
        "message": "Cannot dispute escrow in state: disputed. Must be: paid, sent, received"
    }
}

List your disputes ​

GET/v1/disputes

Disputes on escrows where you are the buyer or the seller, newest first, with the escrow's item, amount, status and both parties.

  • Auth: A personal API key (Authorization: Bearer <key>) or a session cookie with X-CSRF-Token on writes.
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not needed. Reading has no side effects.

Query parameters ​

NameTypeRequiredDescription
statusstringNoOnly this dispute status, for example open
pageintegerNoDefault 1
limitintegerNo1 to 50. Default 20

Example request ​

bash
curl "https://api.rekberpay.com/v1/disputes?limit=5" \
  -H "Authorization: Bearer $REKBERPAY_API_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/disputes?limit=5', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_API_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/disputes?limit=5",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_API_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/disputes?limit=5');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_API_KEY'),
    ],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": [
        {
            "id": "77777771-7777-4777-8777-777777777777",
            "escrow_id": "55555551-5555-4555-8555-555555555555",
            "initiator_id": "11111110-1111-4111-8111-111111111111",
            "reason_category": "item_not_as_described",
            "description": "Logo yang dikirim tidak sesuai brief: warna dan font berbeda dari yang disepakati.",
            "status": "open",
            "priority": "normal",
            "resolution_note": null,
            "resolved_by": null,
            "resolved_at": null,
            "created_at": "2026-10-13T14:23:18.823Z",
            "updated_at": "2026-10-13T14:23:18.823Z",
            "item_name": "Jasa desain logo toko",
            "amount": "300000.00",
            "escrow_status": "disputed",
            "buyer_id": "11111110-1111-4111-8111-111111111111",
            "seller_id": "44444440-4444-4444-8444-444444444444",
            "buyer_email": "[email protected]",
            "buyer_full_name": "Rina Wijaya",
            "seller_email": null,
            "seller_full_name": "Budi Santoso"
        }
    ],
    "pagination": {
        "page": 1,
        "limit": 5,
        "total": "1"
    }
}

Errors ​

StatusCodeCause
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or API key, the session expired, or the key is wrong or revoked
401WORKSPACE_KEY_ONLYA workspace key (rpe_...) was sent. Those only work under /v1/enterprise
403ACCOUNT_INACTIVE, SUSPENDEDThe account is closed, being deleted or suspended
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Get a dispute ​

GET/v1/disputes/:id
  • Auth: A personal API key (Authorization: Bearer <key>) or a session cookie with X-CSRF-Token on writes.
  • Who can call: The buyer, the seller or a RekberPay admin.
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not needed. Reading has no side effects.

Path parameters ​

NameTypeDescription
idUUIDThe dispute ID

Example request ​

bash
curl https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777 \
  -H "Authorization: Bearer $REKBERPAY_API_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_API_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_API_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_API_KEY'),
    ],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "id": "77777771-7777-4777-8777-777777777777",
        "escrow_id": "55555551-5555-4555-8555-555555555555",
        "initiator_id": "11111110-1111-4111-8111-111111111111",
        "reason_category": "item_not_as_described",
        "description": "Logo yang dikirim tidak sesuai brief: warna dan font berbeda dari yang disepakati.",
        "status": "open",
        "priority": "normal",
        "resolution_note": null,
        "resolved_by": null,
        "resolved_at": null,
        "created_at": "2026-10-13T14:23:18.823Z",
        "updated_at": "2026-10-13T14:23:18.823Z",
        "item_name": "Jasa desain logo toko",
        "amount": "300000.00",
        "escrow_status": "disputed",
        "buyer_id": "11111110-1111-4111-8111-111111111111",
        "seller_id": "44444440-4444-4444-8444-444444444444",
        "buyer_email": "[email protected]",
        "buyer_full_name": "Rina Wijaya",
        "seller_email": null,
        "seller_full_name": "Budi Santoso"
    }
}
FieldTypeDescription
statusstringopen while RekberPay works on it, then the outcome
reason_categorystringThe reason given when it was opened
resolution_notestring | nullRekberPay's note when it is resolved

Errors ​

StatusCodeCause
403FORBIDDENYou are not a party
404NOT_FOUNDNo dispute with this ID
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or API key, the session expired, or the key is wrong or revoked
401WORKSPACE_KEY_ONLYA workspace key (rpe_...) was sent. Those only work under /v1/enterprise
403ACCOUNT_INACTIVE, SUSPENDEDThe account is closed, being deleted or suspended
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

List dispute messages ​

GET/v1/disputes/:id/messages

The dispute thread, oldest first. Each message says who sent it (sender_participant_role: buyer, seller or admin). Send the newest created_at as after and its id as after_id when polling. sender_email is filled only on your own messages.

  • Auth: A personal API key (Authorization: Bearer <key>) or a session cookie with X-CSRF-Token on writes.
  • Who can call: The buyer, the seller or a RekberPay admin.
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not needed. Reading has no side effects.

Path parameters ​

NameTypeDescription
idUUIDThe dispute ID

Query parameters ​

NameTypeRequiredDescription
limitintegerNo1 to 200. Default 100
afterstringNoISO 8601 time. Only messages after it
after_idstringNoThe id of the message at after. With it, the cursor is the pair (created_at, id), so two messages written in the same millisecond are never missed
beforestringNoISO 8601 time. Only messages before it

Example request ​

bash
curl "https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777/messages?limit=20" \
  -H "Authorization: Bearer $REKBERPAY_API_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777/messages?limit=20', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_API_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777/messages?limit=20",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_API_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777/messages?limit=20');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_API_KEY'),
    ],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": [
        {
            "id": "99999991-9999-4999-8999-999999999999",
            "dispute_id": "77777771-7777-4777-8777-777777777777",
            "sender_id": "11111110-1111-4111-8111-111111111111",
            "body": "Saya lampirkan brief awal dan hasil yang diterima.",
            "attachment_url": "https://api.rekberpay.com/uploads/escrow/1791901398283_862e8cf475cf7693efed4855a91995d0_7e10e097a2087a40e3e8e3b659df3f65.png?exp=1791979200000&sig=c0ffee5b1a9e3d7f4a2c8e6b0d9f1a3c5e7b9d1f3a5c7e9b1d3f5a7c9e1b3d5",
            "created_at": "2026-10-13T14:23:18.835Z",
            "sender_email": "[email protected]",
            "sender_full_name": "Rina Wijaya",
            "sender_role": "user",
            "sender_participant_role": "buyer"
        },
        {
            "id": "aaaaaaa1-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
            "dispute_id": "77777771-7777-4777-8777-777777777777",
            "sender_id": "44444440-4444-4444-8444-444444444444",
            "body": "Revisi bisa saya kirim besok pagi.",
            "attachment_url": null,
            "created_at": "2026-10-13T14:23:18.841Z",
            "sender_email": null,
            "sender_full_name": "Budi Santoso",
            "sender_role": "user",
            "sender_participant_role": "seller"
        }
    ]
}

Errors ​

StatusCodeCause
403FORBIDDENYou are not a party
404NOT_FOUNDNo dispute with this ID
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or API key, the session expired, or the key is wrong or revoked
401WORKSPACE_KEY_ONLYA workspace key (rpe_...) was sent. Those only work under /v1/enterprise
403ACCOUNT_INACTIVE, SUSPENDEDThe account is closed, being deleted or suspended
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Send a dispute message ​

POST/v1/disputes/:id/messages

Adds a message and up to 10 evidence files. Upload files first with POST /v1/upload/escrow.

  • Auth: A personal API key (Authorization: Bearer <key>) or a session cookie with X-CSRF-Token on writes.
  • Who can call: The buyer, the seller or a RekberPay admin.
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not supported. A retry runs the call again.

Path parameters ​

NameTypeDescription
idUUIDThe dispute ID

Body (JSON) ​

NameTypeRequiredDescription
messagestringYes1 to 5,000 characters
evidence_urlsstring[]NoUp to 10 RekberPay upload URLs

Example request ​

bash
curl -X POST https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777/messages \
  -H "Authorization: Bearer $REKBERPAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Saya lampirkan brief awal dan hasil yang diterima.",
    "evidence_urls": [
      "https://api.rekberpay.com/uploads/escrow/1791901398283_862e8cf475cf7693efed4855a91995d0_7e10e097a2087a40e3e8e3b659df3f65.png"
    ]
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777/messages', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_API_KEY}`,
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        message: "Saya lampirkan brief awal dan hasil yang diterima.",
        evidence_urls: [
            "https://api.rekberpay.com/uploads/escrow/1791901398283_862e8cf475cf7693efed4855a91995d0_7e10e097a2087a40e3e8e3b659df3f65.png"
        ]
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777/messages",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_API_KEY']}",
    },
    json={
        "message": "Saya lampirkan brief awal dan hasil yang diterima.",
        "evidence_urls": [
            "https://api.rekberpay.com/uploads/escrow/1791901398283_862e8cf475cf7693efed4855a91995d0_7e10e097a2087a40e3e8e3b659df3f65.png",
        ],
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/disputes/77777771-7777-4777-8777-777777777777/messages');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'message' => 'Saya lampirkan brief awal dan hasil yang diterima.',
        'evidence_urls' => [
            'https://api.rekberpay.com/uploads/escrow/1791901398283_862e8cf475cf7693efed4855a91995d0_7e10e097a2087a40e3e8e3b659df3f65.png',
        ],
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

201 Created

json
{
    "success": true,
    "data": {
        "id": "99999991-9999-4999-8999-999999999999",
        "dispute_id": "77777771-7777-4777-8777-777777777777",
        "sender_id": "11111110-1111-4111-8111-111111111111",
        "body": "Saya lampirkan brief awal dan hasil yang diterima.",
        "attachment_url": "https://api.rekberpay.com/uploads/escrow/1791901398283_862e8cf475cf7693efed4855a91995d0_7e10e097a2087a40e3e8e3b659df3f65.png?exp=1791979200000&sig=c0ffee5b1a9e3d7f4a2c8e6b0d9f1a3c5e7b9d1f3a5c7e9b1d3f5a7c9e1b3d5",
        "created_at": "2026-10-13T14:23:18.835Z"
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYMessage missing or too long, or an evidence URL is not a RekberPay upload
403FORBIDDENYou are not a party
404NOT_FOUNDNo dispute with this ID
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or API key, the session expired, or the key is wrong or revoked
401WORKSPACE_KEY_ONLYA workspace key (rpe_...) was sent. Those only work under /v1/enterprise
403CSRF_FAILEDSession call without the matching X-CSRF-Token header
403ACCOUNT_INACTIVE, SUSPENDEDThe account is closed, being deleted or suspended
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Escrow, payment services and B2B deals for Indonesian businesses.