Enterprise deals and milestones
A deal holds 1 to 20 milestones. Each milestone is funded, gets evidence, is approved and is released on its own. The deal's total_amount, funded_amount, released_amount and status are always worked out from its milestones; you never set them. Auth, roles, the workspace header and the shared limits are explained in Enterprise overview.
Endpoints
| Method | Path | Endpoint | Auth |
|---|---|---|---|
GET | /v1/enterprise/deals | List deals | viewer |
GET | /v1/enterprise/deals/:id | Get a deal | viewer |
POST | /v1/enterprise/deals | Create a deal | operator |
PATCH | /v1/enterprise/deals/:id | Update a draft deal | operator |
POST | /v1/enterprise/deals/:id/milestones | Add a milestone | operator |
PATCH | /v1/enterprise/deals/:id/milestones/:mid | Update a draft milestone | operator |
POST | /v1/enterprise/deals/:id/activate | Activate a deal | operator |
POST | /v1/enterprise/deals/:id/cancel | Cancel a deal | operator |
POST | /v1/enterprise/deals/:id/milestones/:mid/fund | Fund a milestone (sandbox) | finance |
POST | /v1/enterprise/deals/:id/milestones/:mid/submit-evidence | Submit evidence | operator |
POST | /v1/enterprise/deals/:id/milestones/:mid/dispute | Dispute a milestone | operator |
Milestone and deal statuses
Milestone: draft -> awaiting_funding -> funded -> evidence_submitted -> approved -> released
| | \
| | -> revision_requested -> evidence_submitted
+--------------+--------> disputed (from funded, evidence_submitted or approved)A deal is draft, awaiting_funding, funded, in_progress (at least one milestone released), disputed (any milestone disputed), completed (every live milestone released) or cancelled.
Write routes on this page honour Idempotency-Key. Send one on every create, fund, evidence and dispute call.
List deals
Deals newest first, with amounts, the counterparty name and milestone counts.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role: Any workspace member (
viewerand up). - Rate limit: 300 requests a minute per user, shared by every route.
- Idempotency: Not needed. Reading has no side effects.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
status | string | No | One deal status |
q | string | No | Title or reference contains, up to 100 characters |
page | integer | No | Default 1 |
limit | integer | No | 1 to 50. Default 20 |
Example request
curl "https://api.rekberpay.com/v1/enterprise/deals?limit=2" \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals?limit=2', {
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
},
});
const data = await res.json();import os
import requests
resp = requests.get(
"https://api.rekberpay.com/v1/enterprise/deals?limit=2",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals?limit=2');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": [
{
"id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"reference": "DEAL-2026-0009",
"title": "Packaging run Q4",
"status": "disputed",
"currency": "IDR",
"total_amount": 60000000,
"funded_amount": 50000000,
"released_amount": 30000000,
"cross_border": false,
"counterparty_name": "Harbourline Components Pte Ltd",
"milestone_count": 3,
"released_count": 1,
"created_at": "2026-10-13T14:33:16.361Z",
"updated_at": "2026-10-13T14:33:16.496Z"
},
{
"id": "99999996-9999-4999-8999-999999999999",
"reference": "DEAL-2026-0008",
"title": "Spare parts framework, 2027",
"status": "draft",
"currency": "IDR",
"total_amount": 60000000,
"funded_amount": 0,
"released_amount": 0,
"cross_border": true,
"counterparty_name": "Selangor Precision Parts Sdn Bhd",
"milestone_count": 2,
"released_count": 0,
"created_at": "2026-10-12T18:33:06.162Z",
"updated_at": "2026-10-13T14:33:06.237Z"
}
],
"pagination": {
"page": 1,
"limit": 2,
"total": 9,
"total_pages": 5
}
}Errors
| Status | Code | Cause |
|---|---|---|
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
Get a deal
The deal, every milestone with its approvals, documents and payouts, the last 100 events of the deal and an outcome summary.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role: Any workspace member (
viewerand up). - Rate limit: 300 requests a minute per user, shared by every route.
- Idempotency: Not needed. Reading has no side effects.
Path parameters
| Name | Type | Description |
|---|---|---|
id | UUID | The deal ID |
Example request
curl https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee', {
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
},
});
const data = await res.json();import os
import requests
resp = requests.get(
"https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"deal": {
"id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"reference": "DEAL-2026-0009",
"title": "Packaging run Q4",
"description": "Printed cartons for the Q4 export order, 2 colours.",
"external_ref": "PO-2026-1188",
"status": "disputed",
"currency": "IDR",
"total_amount": 60000000,
"funded_amount": 50000000,
"released_amount": 30000000,
"cross_border": false,
"owner": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"created_at": "2026-10-13T14:33:16.361Z",
"updated_at": "2026-10-13T14:33:16.496Z",
"counterparty": {
"id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
"name": "Harbourline Components Pte Ltd",
"country_code": "SG",
"kyb_status": "verified",
"relationship": "seller"
}
},
"milestones": [
{
"id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"sequence_no": 1,
"title": "Tooling and proof approval",
"required_evidence": "Signed proof sheet",
"amount": 30000000,
"status": "released",
"due_at": null,
"funded_at": "2026-10-13T14:33:16.404Z",
"funding_method": "sandbox",
"evidence_summary": "Proof sheet signed by both sides on 10 October.",
"evidence_submitted_at": "2026-10-13T14:33:16.429Z",
"approved_at": "2026-10-13T14:33:16.456Z",
"released_at": "2026-10-13T14:33:16.480Z",
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [
{
"id": "eeeeeee5-eeee-4eee-8eee-eeeeeeeeeeee",
"approver": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"status": "approved",
"note": "Proof matches the spec sheet.",
"decided_at": "2026-10-13T14:33:16.455Z"
}
],
"documents": [
{
"id": "88888885-8888-4888-8888-888888888888",
"title": "Signed proof sheet",
"document_type": "inspection",
"file_url": "https://files.example.com/northline/proof-sheet-q4.pdf",
"checksum_sha256": "a3f1c2d4e5b60718293a4b5c6d7e8f90112233445566778899aabbccddeeff00",
"visibility": "account",
"uploaded_by": "33333333-3333-4333-8333-333333333333",
"created_at": "2026-10-13T14:33:16.424Z"
}
],
"disbursements": [
{
"id": "55555556-5555-4555-8555-555555555555",
"amount": 5000000,
"status": "queued",
"instruction_label": "Harbourline freight and duties",
"settled_at": null
},
{
"id": "44444446-4444-4444-8444-444444444444",
"amount": 25000000,
"status": "queued",
"instruction_label": "Harbourline IDR collection account",
"settled_at": null
}
]
},
{
"id": "11111115-1111-4111-8111-111111111111",
"sequence_no": 2,
"title": "First 20,000 cartons",
"required_evidence": "Delivery note and inspection report",
"amount": 20000000,
"status": "disputed",
"due_at": null,
"funded_at": "2026-10-13T14:33:16.419Z",
"funding_method": "sandbox",
"evidence_summary": "Inspection report attached as requested by finance.",
"evidence_submitted_at": "2026-10-13T14:33:16.488Z",
"approved_at": null,
"released_at": null,
"disputed_at": "2026-10-13T14:33:16.495Z",
"dispute_reason": "Supplier has not sent the inspection report after two requests.",
"approvals_required": 1,
"approvals": [
{
"id": "22222226-2222-4222-8222-222222222222",
"approver": {
"id": "77777776-7777-4777-8777-777777777777",
"name": "Maya Santoso"
},
"status": "revision_requested",
"note": "Inspection report is missing.",
"decided_at": "2026-10-13T14:33:16.464Z"
},
{
"id": "88888886-8888-4888-8888-888888888888",
"approver": null,
"status": "pending",
"note": null,
"decided_at": null
}
],
"documents": [],
"disbursements": []
}
],
"events": [
{
"seq": 140528,
"event": "deal.created",
"entity_type": "deal",
"entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": 50000000,
"summary": "Created deal DEAL-2026-0009: Packaging run Q4",
"created_at": "2026-10-13T14:33:16.361Z"
},
{
"seq": 140529,
"event": "deal.updated",
"entity_type": "deal",
"entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": null,
"summary": "Updated deal DEAL-2026-0009",
"created_at": "2026-10-13T14:33:16.369Z"
}
],
"outcome": {
"time_to_release_minutes": 0,
"approvals_by_people": 2,
"revisions": 1
}
}
}Lists are cut to the first 2 items and long text is shortened here.
| Field | Type | Description |
|---|---|---|
deal | object | The deal with its reference (for example DEAL-2026-0013), amounts, owner and counterparty |
milestones | array | Each with status, amount, approvals_required, approvals, documents, disbursements and timestamps |
events | array | The deal's latest events, newest first |
outcome | object | time_to_release_minutes, approvals_by_people and revisions |
Errors
| Status | Code | Cause |
|---|---|---|
404 | NOT_FOUND | No deal with this ID in the workspace |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
Create a deal
Creates a draft deal with its milestones. Nothing can be funded until you activate it.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role:
operator,finance,adminorowner. - Rate limit: 300 requests a minute per user, shared by every route.
- Idempotency: Send an
Idempotency-Keyheader (UUID v4). A retry with the same key and body returns the first answer withIdempotent-Replay: true.
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
title | string | Yes | 2 to 180 characters |
milestones | array | Yes | 1 to 20 milestones, see below |
counterparty_id | UUID | No | A counterparty of the workspace. Needed before activation |
description | string | No | At most 1,000 characters |
external_ref | string | No | Your own reference, for example a PO number. At most 80 characters |
currency | string | No | Three letters. Default IDR |
cross_border | boolean | No | Default false |
fee_allocation | string | No | Who pays the platform fee: buyer (your workspace), seller (the counterparty) or split. Default: the workspace default split |
fee_buyer_percent | integer | No | 0 to 100, your workspace's share of the fee. Required with split |
The answer carries the fee snapshot in deal.fee and each milestone's buyer_fee, seller_fee, funding_amount and payout_amount. The rate and minimum are taken from your workspace pricing now and never change for this deal; see Enterprise platform fee.
Each milestone:
| Name | Type | Required | Description |
|---|---|---|---|
title | string | Yes | 2 to 180 characters |
amount | integer | Yes | Whole rupiah, 10,000 to 50,000,000,000 |
required_evidence | string | No | What the supplier must show, at most 300 characters |
due_at | string | No | ISO 8601 date-time with offset |
Example request
curl -X POST https://api.rekberpay.com/v1/enterprise/deals \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: ddddddd4-dddd-4ddd-8ddd-dddddddddddd" \
-H "Content-Type: application/json" \
-d '{
"title": "Packaging run Q4",
"description": "Printed cartons for the Q4 export order.",
"external_ref": "PO-2026-1188",
"counterparty_id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
"currency": "IDR",
"milestones": [
{
"title": "Tooling and proof approval",
"amount": 30000000,
"required_evidence": "Signed proof sheet"
},
{
"title": "First 20,000 cartons",
"amount": 20000000,
"required_evidence": "Delivery note and inspection report"
}
]
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'ddddddd4-dddd-4ddd-8ddd-dddddddddddd',
},
body: JSON.stringify({
title: "Packaging run Q4",
description: "Printed cartons for the Q4 export order.",
external_ref: "PO-2026-1188",
counterparty_id: "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
currency: "IDR",
milestones: [
{
title: "Tooling and proof approval",
amount: 30000000,
required_evidence: "Signed proof sheet"
},
{
title: "First 20,000 cartons",
amount: 20000000,
required_evidence: "Delivery note and inspection report"
}
]
}),
});
const data = await res.json();import os
import requests
resp = requests.post(
"https://api.rekberpay.com/v1/enterprise/deals",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "ddddddd4-dddd-4ddd-8ddd-dddddddddddd",
},
json={
"title": "Packaging run Q4",
"description": "Printed cartons for the Q4 export order.",
"external_ref": "PO-2026-1188",
"counterparty_id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
"currency": "IDR",
"milestones": [
{
"title": "Tooling and proof approval",
"amount": 30000000,
"required_evidence": "Signed proof sheet",
},
{
"title": "First 20,000 cartons",
"amount": 20000000,
"required_evidence": "Delivery note and inspection report",
},
],
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: ddddddd4-dddd-4ddd-8ddd-dddddddddddd',
],
CURLOPT_POSTFIELDS => json_encode([
'title' => 'Packaging run Q4',
'description' => 'Printed cartons for the Q4 export order.',
'external_ref' => 'PO-2026-1188',
'counterparty_id' => 'eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee',
'currency' => 'IDR',
'milestones' => [
[
'title' => 'Tooling and proof approval',
'amount' => 30000000,
'required_evidence' => 'Signed proof sheet',
],
[
'title' => 'First 20,000 cartons',
'amount' => 20000000,
'required_evidence' => 'Delivery note and inspection report',
],
],
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
201 Created
{
"success": true,
"data": {
"deal": {
"id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"reference": "DEAL-2026-0009",
"title": "Packaging run Q4",
"description": "Printed cartons for the Q4 export order.",
"external_ref": "PO-2026-1188",
"status": "draft",
"currency": "IDR",
"total_amount": 50000000,
"funded_amount": 0,
"released_amount": 0,
"cross_border": false,
"owner": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"created_at": "2026-10-13T14:33:16.361Z",
"updated_at": "2026-10-13T14:33:16.361Z",
"counterparty": {
"id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
"name": "Harbourline Components Pte Ltd",
"country_code": "SG",
"kyb_status": "verified",
"relationship": "seller"
}
},
"milestones": [
{
"id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"sequence_no": 1,
"title": "Tooling and proof approval",
"required_evidence": "Signed proof sheet",
"amount": 30000000,
"status": "draft",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
},
{
"id": "11111115-1111-4111-8111-111111111111",
"sequence_no": 2,
"title": "First 20,000 cartons",
"required_evidence": "Delivery note and inspection report",
"amount": 20000000,
"status": "draft",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
}
],
"events": [
{
"seq": 140528,
"event": "deal.created",
"entity_type": "deal",
"entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": 50000000,
"summary": "Created deal DEAL-2026-0009: Packaging run Q4",
"created_at": "2026-10-13T14:33:16.361Z"
}
],
"outcome": {
"time_to_release_minutes": null,
"approvals_by_people": 0,
"revisions": 0
}
}
}Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | A field failed validation. error.details.fieldErrors names it |
404 | COUNTERPARTY_NOT_FOUND | The counterparty is not in this workspace |
409 | DEAL_CREATE_BUSY | Another deal is being created at the same moment. Try again |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below operator |
403 | SCOPE_REQUIRED | The workspace key only has the read scope |
403 | CSRF_FAILED | Session call without the matching X-CSRF-Token header |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
Update a draft deal
Changes deal fields while the deal is a draft. Returns the full deal, like Get a deal.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role:
operator,finance,adminorowner. - Rate limit: 300 requests a minute per user, shared by every route.
- Idempotency: Send an
Idempotency-Keyheader (UUID v4). A retry with the same key and body returns the first answer withIdempotent-Replay: true.
Path parameters
| Name | Type | Description |
|---|---|---|
id | UUID | The deal ID |
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
title | string | No | 2 to 180 characters |
description | string | null | No | At most 1,000 characters. null clears it |
external_ref | string | null | No | At most 80 characters |
counterparty_id | UUID | null | No | A counterparty of the workspace |
cross_border | boolean | No | Cross-border flag |
fee_allocation | string | No | buyer, seller or split: change who pays the platform fee while the deal is a draft |
fee_buyer_percent | integer | No | 0 to 100. Required with split |
Example request
curl -X PATCH https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: 22222225-2222-4222-8222-222222222222" \
-H "Content-Type: application/json" \
-d '{
"description": "Printed cartons for the Q4 export order, 2 colours."
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '22222225-2222-4222-8222-222222222222',
},
body: JSON.stringify({
description: "Printed cartons for the Q4 export order, 2 colours."
}),
});
const data = await res.json();import os
import requests
resp = requests.patch(
"https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "22222225-2222-4222-8222-222222222222",
},
json={
"description": "Printed cartons for the Q4 export order, 2 colours.",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: 22222225-2222-4222-8222-222222222222',
],
CURLOPT_POSTFIELDS => json_encode([
'description' => 'Printed cartons for the Q4 export order, 2 colours.',
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"deal": {
"id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"reference": "DEAL-2026-0009",
"title": "Packaging run Q4",
"description": "Printed cartons for the Q4 export order, 2 colours.",
"external_ref": "PO-2026-1188",
"status": "draft",
"currency": "IDR",
"total_amount": 50000000,
"funded_amount": 0,
"released_amount": 0,
"cross_border": false,
"owner": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"created_at": "2026-10-13T14:33:16.361Z",
"updated_at": "2026-10-13T14:33:16.369Z",
"counterparty": {
"id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
"name": "Harbourline Components Pte Ltd",
"country_code": "SG",
"kyb_status": "verified",
"relationship": "seller"
}
},
"milestones": [
{
"id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"sequence_no": 1,
"title": "Tooling and proof approval",
"required_evidence": "Signed proof sheet",
"amount": 30000000,
"status": "draft",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
},
{
"id": "11111115-1111-4111-8111-111111111111",
"sequence_no": 2,
"title": "First 20,000 cartons",
"required_evidence": "Delivery note and inspection report",
"amount": 20000000,
"status": "draft",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
}
],
"events": [
{
"seq": 140528,
"event": "deal.created",
"entity_type": "deal",
"entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": 50000000,
"summary": "Created deal DEAL-2026-0009: Packaging run Q4",
"created_at": "2026-10-13T14:33:16.361Z"
},
{
"seq": 140529,
"event": "deal.updated",
"entity_type": "deal",
"entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": null,
"summary": "Updated deal DEAL-2026-0009",
"created_at": "2026-10-13T14:33:16.369Z"
}
],
"outcome": {
"time_to_release_minutes": null,
"approvals_by_people": 0,
"revisions": 0
}
}
}Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | A field failed validation, or nothing to update |
404 | NOT_FOUND, COUNTERPARTY_NOT_FOUND | No such deal, or the counterparty is not in this workspace |
409 | NOT_DRAFT | Only draft deals can be edited |
409 | CONCURRENT_MODIFICATION | The deal changed while the call ran |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below operator |
403 | SCOPE_REQUIRED | The workspace key only has the read scope |
403 | CSRF_FAILED | Session call without the matching X-CSRF-Token header |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
A real NOT_DRAFT answer:
409 Conflict
{
"success": false,
"error": {
"code": "NOT_DRAFT",
"message": "Only draft deals can be edited."
}
}Add a milestone
Adds one milestone to a draft deal. Returns 201 with the full deal.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role:
operator,finance,adminorowner. - Rate limit: 300 requests a minute per user, shared by every route.
- Idempotency: Send an
Idempotency-Keyheader (UUID v4). A retry with the same key and body returns the first answer withIdempotent-Replay: true.
Path parameters
| Name | Type | Description |
|---|---|---|
id | UUID | The deal ID |
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
title | string | Yes | 2 to 180 characters |
amount | integer | Yes | 10,000 to 50,000,000,000 |
required_evidence | string | No | At most 300 characters |
due_at | string | No | ISO 8601 date-time with offset |
Example request
curl -X POST https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: 33333335-3333-4333-8333-333333333333" \
-H "Content-Type: application/json" \
-d '{
"title": "Final 10,000 cartons",
"amount": 10000000,
"required_evidence": "Delivery note"
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '33333335-3333-4333-8333-333333333333',
},
body: JSON.stringify({
title: "Final 10,000 cartons",
amount: 10000000,
required_evidence: "Delivery note"
}),
});
const data = await res.json();import os
import requests
resp = requests.post(
"https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "33333335-3333-4333-8333-333333333333",
},
json={
"title": "Final 10,000 cartons",
"amount": 10000000,
"required_evidence": "Delivery note",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: 33333335-3333-4333-8333-333333333333',
],
CURLOPT_POSTFIELDS => json_encode([
'title' => 'Final 10,000 cartons',
'amount' => 10000000,
'required_evidence' => 'Delivery note',
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
201 Created
{
"success": true,
"data": {
"deal": {
"id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"reference": "DEAL-2026-0009",
"title": "Packaging run Q4",
"description": "Printed cartons for the Q4 export order, 2 colours.",
"external_ref": "PO-2026-1188",
"status": "draft",
"currency": "IDR",
"total_amount": 60000000,
"funded_amount": 0,
"released_amount": 0,
"cross_border": false,
"owner": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"created_at": "2026-10-13T14:33:16.361Z",
"updated_at": "2026-10-13T14:33:16.376Z",
"counterparty": {
"id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
"name": "Harbourline Components Pte Ltd",
"country_code": "SG",
"kyb_status": "verified",
"relationship": "seller"
}
},
"milestones": [
{
"id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"sequence_no": 1,
"title": "Tooling and proof approval",
"required_evidence": "Signed proof sheet",
"amount": 30000000,
"status": "draft",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
},
{
"id": "11111115-1111-4111-8111-111111111111",
"sequence_no": 2,
"title": "First 20,000 cartons",
"required_evidence": "Delivery note and inspection report",
"amount": 20000000,
"status": "draft",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
},
{
"id": "44444445-4444-4444-8444-444444444444",
"sequence_no": 3,
"title": "Final 10,000 cartons",
"required_evidence": "Delivery note",
"amount": 10000000,
"status": "draft",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
}
],
"events": [
{
"seq": 140528,
"event": "deal.created",
"entity_type": "deal",
"entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": 50000000,
"summary": "Created deal DEAL-2026-0009: Packaging run Q4",
"created_at": "2026-10-13T14:33:16.361Z"
},
{
"seq": 140529,
"event": "deal.updated",
"entity_type": "deal",
"entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": null,
"summary": "Updated deal DEAL-2026-0009",
"created_at": "2026-10-13T14:33:16.369Z"
},
{
"seq": 140530,
"event": "milestone.added",
"entity_type": "milestone",
"entity_id": "44444445-4444-4444-8444-444444444444",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": 10000000,
"summary": "Added milestone Final 10,000 cartons to DEAL-2026-0009",
"created_at": "2026-10-13T14:33:16.376Z"
}
],
"outcome": {
"time_to_release_minutes": null,
"approvals_by_people": 0,
"revisions": 0
}
}
}Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | A field failed validation |
404 | NOT_FOUND | No such deal |
409 | NOT_DRAFT | Only draft deals can be edited |
409 | TOO_MANY_MILESTONES | The deal already has 20 milestones |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below operator |
403 | SCOPE_REQUIRED | The workspace key only has the read scope |
403 | CSRF_FAILED | Session call without the matching X-CSRF-Token header |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
Update a draft milestone
Changes a milestone while it and its deal are drafts. Returns the full deal.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role:
operator,finance,adminorowner. - Rate limit: 300 requests a minute per user, shared by every route.
- Idempotency: Send an
Idempotency-Keyheader (UUID v4). A retry with the same key and body returns the first answer withIdempotent-Replay: true.
Path parameters
| Name | Type | Description |
|---|---|---|
id | UUID | The deal ID |
mid | UUID | The milestone ID |
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
title | string | No | 2 to 180 characters |
amount | integer | No | 10,000 to 50,000,000,000 |
required_evidence | string | null | No | At most 300 characters |
due_at | string | null | No | ISO 8601 date-time with offset |
Example request
curl -X PATCH https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/44444445-4444-4444-8444-444444444444 \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: 55555555-5555-4555-8555-555555555555" \
-H "Content-Type: application/json" \
-d '{
"due_at": "2026-12-15T00:00:00+07:00"
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/44444445-4444-4444-8444-444444444444', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '55555555-5555-4555-8555-555555555555',
},
body: JSON.stringify({
due_at: "2026-12-15T00:00:00+07:00"
}),
});
const data = await res.json();import os
import requests
resp = requests.patch(
"https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/44444445-4444-4444-8444-444444444444",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "55555555-5555-4555-8555-555555555555",
},
json={
"due_at": "2026-12-15T00:00:00+07:00",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/44444445-4444-4444-8444-444444444444');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: 55555555-5555-4555-8555-555555555555',
],
CURLOPT_POSTFIELDS => json_encode([
'due_at' => '2026-12-15T00:00:00+07:00',
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"deal": {
"id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"reference": "DEAL-2026-0009",
"title": "Packaging run Q4",
"description": "Printed cartons for the Q4 export order, 2 colours.",
"external_ref": "PO-2026-1188",
"status": "draft",
"currency": "IDR",
"total_amount": 60000000,
"funded_amount": 0,
"released_amount": 0,
"cross_border": false,
"owner": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"created_at": "2026-10-13T14:33:16.361Z",
"updated_at": "2026-10-13T14:33:16.376Z",
"counterparty": {
"id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
"name": "Harbourline Components Pte Ltd",
"country_code": "SG",
"kyb_status": "verified",
"relationship": "seller"
}
},
"milestones": [
{
"id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"sequence_no": 1,
"title": "Tooling and proof approval",
"required_evidence": "Signed proof sheet",
"amount": 30000000,
"status": "draft",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
},
{
"id": "11111115-1111-4111-8111-111111111111",
"sequence_no": 2,
"title": "First 20,000 cartons",
"required_evidence": "Delivery note and inspection report",
"amount": 20000000,
"status": "draft",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
},
{
"id": "44444445-4444-4444-8444-444444444444",
"sequence_no": 3,
"title": "Final 10,000 cartons",
"required_evidence": "Delivery note",
"amount": 10000000,
"status": "draft",
"due_at": "2026-12-14T17:00:00.000Z",
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
}
],
"events": [
{
"seq": 140528,
"event": "deal.created",
"entity_type": "deal",
"entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": 50000000,
"summary": "Created deal DEAL-2026-0009: Packaging run Q4",
"created_at": "2026-10-13T14:33:16.361Z"
},
{
"seq": 140529,
"event": "deal.updated",
"entity_type": "deal",
"entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": null,
"summary": "Updated deal DEAL-2026-0009",
"created_at": "2026-10-13T14:33:16.369Z"
},
{
"seq": 140530,
"event": "milestone.added",
"entity_type": "milestone",
"entity_id": "44444445-4444-4444-8444-444444444444",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": 10000000,
"summary": "Added milestone Final 10,000 cartons to DEAL-2026-0009",
"created_at": "2026-10-13T14:33:16.376Z"
}
],
"outcome": {
"time_to_release_minutes": null,
"approvals_by_people": 0,
"revisions": 0
}
}
}Lists are cut to the first 3 items and long text is shortened here.
Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | A field failed validation, or nothing to update |
404 | NOT_FOUND | No such deal or milestone |
409 | NOT_DRAFT | Only draft milestones can be edited |
409 | CONCURRENT_MODIFICATION | The milestone changed while the call ran |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below operator |
403 | SCOPE_REQUIRED | The workspace key only has the read scope |
403 | CSRF_FAILED | Session call without the matching X-CSRF-Token header |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
Activate a deal
Moves a draft deal and its milestones to awaiting_funding. The deal needs a counterparty and at least one milestone. Send {} as the body.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role:
operator,finance,adminorowner. - Rate limit: 300 requests a minute per user, shared by every route.
- Idempotency: Send an
Idempotency-Keyheader (UUID v4). A retry with the same key and body returns the first answer withIdempotent-Replay: true.
Path parameters
| Name | Type | Description |
|---|---|---|
id | UUID | The deal ID |
Example request
curl -X POST https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/activate \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: 66666665-6666-4666-8666-666666666666" \
-H "Content-Type: application/json" \
-d '{}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/activate', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '66666665-6666-4666-8666-666666666666',
},
body: JSON.stringify({}),
});
const data = await res.json();import os
import requests
resp = requests.post(
"https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/activate",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "66666665-6666-4666-8666-666666666666",
},
json={},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/activate');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: 66666665-6666-4666-8666-666666666666',
],
CURLOPT_POSTFIELDS => json_encode(new stdClass()),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"deal": {
"id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"reference": "DEAL-2026-0009",
"title": "Packaging run Q4",
"description": "Printed cartons for the Q4 export order, 2 colours.",
"external_ref": "PO-2026-1188",
"status": "awaiting_funding",
"currency": "IDR",
"total_amount": 60000000,
"funded_amount": 0,
"released_amount": 0,
"cross_border": false,
"owner": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"created_at": "2026-10-13T14:33:16.361Z",
"updated_at": "2026-10-13T14:33:16.391Z",
"counterparty": {
"id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
"name": "Harbourline Components Pte Ltd",
"country_code": "SG",
"kyb_status": "verified",
"relationship": "seller"
}
},
"milestones": [
{
"id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"sequence_no": 1,
"title": "Tooling and proof approval",
"required_evidence": "Signed proof sheet",
"amount": 30000000,
"status": "awaiting_funding",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
},
{
"id": "11111115-1111-4111-8111-111111111111",
"sequence_no": 2,
"title": "First 20,000 cartons",
"required_evidence": "Delivery note and inspection report",
"amount": 20000000,
"status": "awaiting_funding",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
}
],
"events": [
{
"seq": 140528,
"event": "deal.created",
"entity_type": "deal",
"entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": 50000000,
"summary": "Created deal DEAL-2026-0009: Packaging run Q4",
"created_at": "2026-10-13T14:33:16.361Z"
},
{
"seq": 140529,
"event": "deal.updated",
"entity_type": "deal",
"entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": null,
"summary": "Updated deal DEAL-2026-0009",
"created_at": "2026-10-13T14:33:16.369Z"
}
],
"outcome": {
"time_to_release_minutes": null,
"approvals_by_people": 0,
"revisions": 0
}
}
}Lists are cut to the first 2 items and long text is shortened here.
Errors
| Status | Code | Cause |
|---|---|---|
404 | NOT_FOUND | No such deal |
409 | NOT_DRAFT | The deal is not a draft |
409 | COUNTERPARTY_REQUIRED | Set a counterparty first |
409 | NO_MILESTONES | Add at least one milestone first |
409 | CONCURRENT_MODIFICATION | The deal changed while the call ran |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below operator |
403 | SCOPE_REQUIRED | The workspace key only has the read scope |
403 | CSRF_FAILED | Session call without the matching X-CSRF-Token header |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
Cancel a deal
Cancels a deal while no milestone is funded. Once money is held, open a dispute instead. Send {} as the body.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role:
operator,finance,adminorowner. - Rate limit: 300 requests a minute per user, shared by every route.
- Idempotency: Send an
Idempotency-Keyheader (UUID v4). A retry with the same key and body returns the first answer withIdempotent-Replay: true.
Path parameters
| Name | Type | Description |
|---|---|---|
id | UUID | The deal ID |
Example request
curl -X POST https://api.rekberpay.com/v1/enterprise/deals/fffffff6-ffff-4fff-8fff-ffffffffffff/cancel \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: 11111117-1111-4111-8111-111111111111" \
-H "Content-Type: application/json" \
-d '{}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/fffffff6-ffff-4fff-8fff-ffffffffffff/cancel', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '11111117-1111-4111-8111-111111111111',
},
body: JSON.stringify({}),
});
const data = await res.json();import os
import requests
resp = requests.post(
"https://api.rekberpay.com/v1/enterprise/deals/fffffff6-ffff-4fff-8fff-ffffffffffff/cancel",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "11111117-1111-4111-8111-111111111111",
},
json={},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/fffffff6-ffff-4fff-8fff-ffffffffffff/cancel');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: 11111117-1111-4111-8111-111111111111',
],
CURLOPT_POSTFIELDS => json_encode(new stdClass()),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"deal": {
"id": "fffffff6-ffff-4fff-8fff-ffffffffffff",
"reference": "DEAL-2026-0010",
"title": "Sample shipment",
"description": null,
"external_ref": null,
"status": "cancelled",
"currency": "IDR",
"total_amount": 2500000,
"funded_amount": 0,
"released_amount": 0,
"cross_border": false,
"owner": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"created_at": "2026-10-13T14:33:16.555Z",
"updated_at": "2026-10-13T14:33:16.563Z",
"counterparty": {
"id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
"name": "Harbourline Components Pte Ltd",
"country_code": "SG",
"kyb_status": "verified",
"relationship": "seller"
}
},
"milestones": [
{
"id": "22222227-2222-4222-8222-222222222222",
"sequence_no": 1,
"title": "Samples",
"required_evidence": null,
"amount": 2500000,
"status": "cancelled",
"due_at": null,
"funded_at": null,
"funding_method": null,
"evidence_summary": null,
"evidence_submitted_at": null,
"approved_at": null,
"released_at": null,
"disputed_at": null,
"dispute_reason": null,
"approvals_required": 1,
"approvals": [],
"documents": [],
"disbursements": []
}
],
"events": [
{
"seq": 140555,
"event": "deal.created",
"entity_type": "deal",
"entity_id": "fffffff6-ffff-4fff-8fff-ffffffffffff",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": 2500000,
"summary": "Created deal DEAL-2026-0010: Sample shipment",
"created_at": "2026-10-13T14:33:16.556Z"
},
{
"seq": 140556,
"event": "deal.cancelled",
"entity_type": "deal",
"entity_id": "fffffff6-ffff-4fff-8fff-ffffffffffff",
"actor": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"amount": null,
"summary": "Cancelled deal DEAL-2026-0010",
"created_at": "2026-10-13T14:33:16.564Z"
}
],
"outcome": {
"time_to_release_minutes": null,
"approvals_by_people": 0,
"revisions": 0
}
}
}Errors
| Status | Code | Cause |
|---|---|---|
404 | NOT_FOUND | No such deal |
409 | INVALID_STATE | The deal is already cancelled |
409 | NOT_CANCELLABLE | A milestone is already funded |
409 | CONCURRENT_MODIFICATION | The deal changed while the call ran |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below operator |
403 | SCOPE_REQUIRED | The workspace key only has the read scope |
403 | CSRF_FAILED | Session call without the matching X-CSRF-Token header |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
Fund a milestone (sandbox)
Sandbox workspaces only: marks an awaiting_funding milestone as funded so you can test the rest of the flow. In a live workspace RekberPay confirms funding when the money arrives, and this call answers 403 LIVE_FUNDING_IS_PLATFORM_CONFIRMED.
Sandbox funding is always recorded as sandbox, whatever method you send. Deals made in the sandbox carry is_test: true. When the workspace goes live, their unfinished milestones are cancelled and they never release real money.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role:
finance,adminorowner. - Rate limit: 30 a minute per user for this route, plus the shared 300 a minute.
- Idempotency: Send an
Idempotency-Keyheader (UUID v4). A retry with the same key and body returns the first answer withIdempotent-Replay: true.
Path parameters
| Name | Type | Description |
|---|---|---|
id | UUID | The deal ID |
mid | UUID | The milestone ID |
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
method | string | Yes | qris, va, ewallet, wire or sandbox |
reference | string | No | Your test reference, 1 to 120 characters |
Example request
curl -X POST https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/fffffff4-ffff-4fff-8fff-ffffffffffff/fund \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: 77777775-7777-4777-8777-777777777777" \
-H "Content-Type: application/json" \
-d '{
"method": "sandbox",
"reference": "SANDBOX-TOPUP-0001"
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/fffffff4-ffff-4fff-8fff-ffffffffffff/fund', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '77777775-7777-4777-8777-777777777777',
},
body: JSON.stringify({
method: "sandbox",
reference: "SANDBOX-TOPUP-0001"
}),
});
const data = await res.json();import os
import requests
resp = requests.post(
"https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/fffffff4-ffff-4fff-8fff-ffffffffffff/fund",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "77777775-7777-4777-8777-777777777777",
},
json={
"method": "sandbox",
"reference": "SANDBOX-TOPUP-0001",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/fffffff4-ffff-4fff-8fff-ffffffffffff/fund');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: 77777775-7777-4777-8777-777777777777',
],
CURLOPT_POSTFIELDS => json_encode([
'method' => 'sandbox',
'reference' => 'SANDBOX-TOPUP-0001',
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"milestone": {
"id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"deal_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"status": "funded",
"amount": 30000000,
"buyer_fee": 600000,
"funding_amount": 30600000,
"funding_method": "sandbox"
},
"deal_status": "funded"
}
}Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | method missing or not valid |
403 | LIVE_FUNDING_IS_PLATFORM_CONFIRMED | The workspace is live |
404 | NOT_FOUND | No such milestone |
409 | CONCURRENT_MODIFICATION | The milestone is not awaiting_funding |
409 | LIVE_DEAL_IN_SANDBOX | The deal is a live deal (is_test: false). Sandbox test money only funds deals created in the sandbox |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below finance |
403 | SCOPE_REQUIRED | The workspace key only has the read scope |
403 | CSRF_FAILED | Session call without the matching X-CSRF-Token header |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
Submit evidence
Sends a funded milestone for approval with a short summary and the documents that prove the work. Register the files first with POST /v1/enterprise/documents. Works on funded and revision_requested milestones; each submission opens a new approval round. approvals_required is 2 when the amount is at or above the workspace's four_eyes_threshold.
The person who submits cannot decide on the same milestone, so submit with a different user or key than the one that approves.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role:
operator,finance,adminorowner. - Rate limit: 30 a minute per user for this route, plus the shared 300 a minute.
- Idempotency: Send an
Idempotency-Keyheader (UUID v4). A retry with the same key and body returns the first answer withIdempotent-Replay: true.
Path parameters
| Name | Type | Description |
|---|---|---|
id | UUID | The deal ID |
mid | UUID | The milestone ID |
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
evidence_summary | string | Yes | 10 to 500 characters |
document_ids | UUID[] | No | Up to 10 documents of this workspace, deal or milestone |
Example request
curl -X POST https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/11111115-1111-4111-8111-111111111111/submit-evidence \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: 99999995-9999-4999-8999-999999999999" \
-H "Content-Type: application/json" \
-d '{
"evidence_summary": "First 20,000 cartons delivered to the Cikarang warehouse.",
"document_ids": []
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/11111115-1111-4111-8111-111111111111/submit-evidence', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '99999995-9999-4999-8999-999999999999',
},
body: JSON.stringify({
evidence_summary: "First 20,000 cartons delivered to the Cikarang warehouse.",
document_ids: []
}),
});
const data = await res.json();import os
import requests
resp = requests.post(
"https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/11111115-1111-4111-8111-111111111111/submit-evidence",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "99999995-9999-4999-8999-999999999999",
},
json={
"evidence_summary": "First 20,000 cartons delivered to the Cikarang warehouse.",
"document_ids": [],
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/11111115-1111-4111-8111-111111111111/submit-evidence');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: 99999995-9999-4999-8999-999999999999',
],
CURLOPT_POSTFIELDS => json_encode([
'evidence_summary' => 'First 20,000 cartons delivered to the Cikarang warehouse.',
'document_ids' => [],
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"milestone_id": "11111115-1111-4111-8111-111111111111",
"status": "evidence_submitted",
"approvals_required": 1,
"deal_status": "funded"
}
}Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | Summary too short or too long |
400 | INVALID_DOCUMENTS | A document is not in this workspace, deal or milestone |
404 | NOT_FOUND | No such milestone |
409 | INVALID_STATE | The milestone is not funded or revision_requested |
409 | CONCURRENT_MODIFICATION | The milestone changed while the call ran |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below operator |
403 | SCOPE_REQUIRED | The workspace key only has the read scope |
403 | CSRF_FAILED | Session call without the matching X-CSRF-Token header |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
Dispute a milestone
Freezes a funded, evidence_submitted or approved milestone. RekberPay operations looks at it and resolves it to funded, released or cancelled; the result shows up in the event feed.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role:
operator,finance,adminorowner. - Rate limit: 30 a minute per user for this route, plus the shared 300 a minute.
- Idempotency: Send an
Idempotency-Keyheader (UUID v4). A retry with the same key and body returns the first answer withIdempotent-Replay: true.
Path parameters
| Name | Type | Description |
|---|---|---|
id | UUID | The deal ID |
mid | UUID | The milestone ID |
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
reason | string | Yes | 10 to 700 characters |
Example request
curl -X POST https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/11111115-1111-4111-8111-111111111111/dispute \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: 66666666-6666-4666-8666-666666666666" \
-H "Content-Type: application/json" \
-d '{
"reason": "Supplier has not sent the inspection report after two requests."
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/11111115-1111-4111-8111-111111111111/dispute', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '66666666-6666-4666-8666-666666666666',
},
body: JSON.stringify({
reason: "Supplier has not sent the inspection report after two requests."
}),
});
const data = await res.json();import os
import requests
resp = requests.post(
"https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/11111115-1111-4111-8111-111111111111/dispute",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "66666666-6666-4666-8666-666666666666",
},
json={
"reason": "Supplier has not sent the inspection report after two requests.",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/11111115-1111-4111-8111-111111111111/dispute');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: 66666666-6666-4666-8666-666666666666',
],
CURLOPT_POSTFIELDS => json_encode([
'reason' => 'Supplier has not sent the inspection report after two requests.',
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"milestone_id": "11111115-1111-4111-8111-111111111111",
"status": "disputed",
"deal_status": "disputed"
}
}Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | Reason too short or too long |
404 | NOT_FOUND | No such milestone |
409 | INVALID_STATE | The milestone cannot be disputed in its current state |
409 | CONCURRENT_MODIFICATION | The milestone changed while the call ran |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below operator |
403 | SCOPE_REQUIRED | The workspace key only has the read scope |
403 | CSRF_FAILED | Session call without the matching X-CSRF-Token header |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |