Milestone deal end to end
This walkthrough runs a packaging order with a supplier, Harbourline Components, in a sandbox workspace. It uses a workspace API key with the read and write scopes (see Authentication and API keys). Every request and answer below is from the same run.
1. Create the deal
A deal starts as a draft with one or more milestones. Each milestone is funded, proven and released on its own.
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);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
}
}
}2. Adjust it while it is a draft
Change deal fields:
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);Add a third milestone:
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);Set a due date on it:
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);Each of these answers with the full deal, like step 1.
3. Activate
Activation locks the plan. The deal and its milestones move to awaiting_funding.
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);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": []
}
],
"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
}
}
}Lists are cut to the first 1 items and long text is shortened here.
From now on the deal fields cannot change:
409 Conflict
{
"success": false,
"error": {
"code": "NOT_DRAFT",
"message": "Only draft deals can be edited."
}
}4. Fund the first milestone
In the sandbox a finance member marks the milestone funded. In a live workspace RekberPay does this when the money arrives.
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);200 OK
{
"success": true,
"data": {
"milestone": {
"id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"deal_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"status": "funded",
"amount": 30000000,
"funding_method": "sandbox"
},
"deal_status": "funded"
}
}5. Register the evidence
Register the file as a document on the milestone:
curl -X POST https://api.rekberpay.com/v1/enterprise/documents \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Signed proof sheet",
"document_type": "inspection",
"file_url": "https://files.example.com/northline/proof-sheet-q4.pdf",
"checksum_sha256": "a3f1c2d4e5b60718293a4b5c6d7e8f90112233445566778899aabbccddeeff00",
"milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff"
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/documents', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: "Signed proof sheet",
document_type: "inspection",
file_url: "https://files.example.com/northline/proof-sheet-q4.pdf",
checksum_sha256: "a3f1c2d4e5b60718293a4b5c6d7e8f90112233445566778899aabbccddeeff00",
milestone_id: "fffffff4-ffff-4fff-8fff-ffffffffffff"
}),
});
const data = await res.json();import os
import requests
resp = requests.post(
"https://api.rekberpay.com/v1/enterprise/documents",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
},
json={
"title": "Signed proof sheet",
"document_type": "inspection",
"file_url": "https://files.example.com/northline/proof-sheet-q4.pdf",
"checksum_sha256": "a3f1c2d4e5b60718293a4b5c6d7e8f90112233445566778899aabbccddeeff00",
"milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/documents');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'title' => 'Signed proof sheet',
'document_type' => 'inspection',
'file_url' => 'https://files.example.com/northline/proof-sheet-q4.pdf',
'checksum_sha256' => 'a3f1c2d4e5b60718293a4b5c6d7e8f90112233445566778899aabbccddeeff00',
'milestone_id' => 'fffffff4-ffff-4fff-8fff-ffffffffffff',
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);201 Created
{
"success": true,
"data": {
"id": "88888885-8888-4888-8888-888888888888",
"deal_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"document_type": "inspection",
"title": "Signed proof sheet",
"file_url": "https://files.example.com/northline/proof-sheet-q4.pdf",
"checksum_sha256": "a3f1c2d4e5b60718293a4b5c6d7e8f90112233445566778899aabbccddeeff00",
"visibility": "account",
"uploaded_by": "33333333-3333-4333-8333-333333333333"
}
}Then the supplier-facing team submits the evidence. In this run an operator submitted the first milestone from the dashboard, and the key submitted the second:
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);200 OK
{
"success": true,
"data": {
"milestone_id": "11111115-1111-4111-8111-111111111111",
"status": "evidence_submitted",
"approvals_required": 1,
"deal_status": "funded"
}
}6. Decide
The approval queue lists what is waiting:
curl "https://api.rekberpay.com/v1/enterprise/approvals?limit=2" \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"const res = await fetch('https://api.rekberpay.com/v1/enterprise/approvals?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/approvals?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/approvals?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);200 OK
{
"success": true,
"data": [
{
"milestone_id": "aaaaaaa5-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"deal_id": "bbbbbbb5-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
"deal_reference": "DEAL-2026-0003",
"deal_title": "Electronic control boards, lot 7",
"milestone_title": "Goods in transit",
"status": "evidence_submitted",
"amount": 150000000,
"currency": "IDR",
"counterparty_name": "Harbourline Components Pte Ltd",
"risk_tier": "standard",
"evidence_summary": "Bill of lading HLCU-5521 and the export declaration for all 600 boards.",
"evidence_submitted_at": "2026-10-13T09:33:06.162Z",
"due_at": "2026-10-15T14:33:06.162Z",
"approvals_required": 2,
"approvals_done": 1,
"my_decision": "approved",
"submitted_by": {
"id": "88888884-8888-4888-8888-888888888888",
"name": "Sari Wibowo"
}
}
],
"pagination": {
"page": 1,
"limit": 2,
"total": 4,
"total_pages": 2
}
}Lists are cut to the first 1 items and long text is shortened here.
The person who submitted evidence cannot decide on it. The key submitted the second milestone, so it is refused there:
409 Conflict
{
"success": false,
"error": {
"code": "SUBMITTER_CANNOT_APPROVE",
"message": "The person who submitted the evidence cannot decide on it."
}
}It approves the first milestone, which an operator submitted:
curl -X POST https://api.rekberpay.com/v1/enterprise/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff/decide \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: fffffff5-ffff-4fff-8fff-ffffffffffff" \
-H "Content-Type: application/json" \
-d '{
"decision": "approved",
"note": "Proof matches the spec sheet."
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff/decide', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'fffffff5-ffff-4fff-8fff-ffffffffffff',
},
body: JSON.stringify({
decision: "approved",
note: "Proof matches the spec sheet."
}),
});
const data = await res.json();import os
import requests
resp = requests.post(
"https://api.rekberpay.com/v1/enterprise/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff/decide",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "fffffff5-ffff-4fff-8fff-ffffffffffff",
},
json={
"decision": "approved",
"note": "Proof matches the spec sheet.",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff/decide');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: fffffff5-ffff-4fff-8fff-ffffffffffff',
],
CURLOPT_POSTFIELDS => json_encode([
'decision' => 'approved',
'note' => 'Proof matches the spec sheet.',
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);200 OK
{
"success": true,
"data": {
"milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"approval_id": "eeeeeee5-eeee-4eee-8eee-eeeeeeeeeeee",
"decision": "approved",
"milestone_status": "approved",
"approvals_required": 1,
"approvals_done": 1
}
}On the second milestone a finance member asks for a revision with a note:
200 OK
{
"success": true,
"data": {
"milestone_id": "11111115-1111-4111-8111-111111111111",
"approval_id": "22222226-2222-4222-8222-222222222222",
"decision": "revision_requested",
"milestone_status": "revision_requested",
"approvals_required": 1,
"approvals_done": 0
}
}Milestones at or above the workspace threshold need two approvers; see Four-eyes approvals.
7. Release
Release the approved milestone to verified payout instructions. Here it is split over two accounts; see Releasing with split payouts.
curl -X POST https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/fffffff4-ffff-4fff-8fff-ffffffffffff/release \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: 33333336-3333-4333-8333-333333333333" \
-H "Content-Type: application/json" \
-d '{
"splits": [
{
"payout_instruction_id": "fffffff3-ffff-4fff-8fff-ffffffffffff",
"amount": 25000000
},
{
"payout_instruction_id": "22222224-2222-4222-8222-222222222222",
"amount": 5000000
}
]
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/fffffff4-ffff-4fff-8fff-ffffffffffff/release', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '33333336-3333-4333-8333-333333333333',
},
body: JSON.stringify({
splits: [
{
payout_instruction_id: "fffffff3-ffff-4fff-8fff-ffffffffffff",
amount: 25000000
},
{
payout_instruction_id: "22222224-2222-4222-8222-222222222222",
amount: 5000000
}
]
}),
});
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/release",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "33333336-3333-4333-8333-333333333333",
},
json={
"splits": [
{
"payout_instruction_id": "fffffff3-ffff-4fff-8fff-ffffffffffff",
"amount": 25000000,
},
{
"payout_instruction_id": "22222224-2222-4222-8222-222222222222",
"amount": 5000000,
},
],
},
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/release');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: 33333336-3333-4333-8333-333333333333',
],
CURLOPT_POSTFIELDS => json_encode([
'splits' => [
[
'payout_instruction_id' => 'fffffff3-ffff-4fff-8fff-ffffffffffff',
'amount' => 25000000,
],
[
'payout_instruction_id' => '22222224-2222-4222-8222-222222222222',
'amount' => 5000000,
],
],
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);200 OK
{
"success": true,
"data": {
"milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"status": "released",
"deal_status": "in_progress",
"disbursements": [
{
"id": "44444446-4444-4444-8444-444444444444",
"payout_instruction_id": "fffffff3-ffff-4fff-8fff-ffffffffffff",
"amount": 25000000,
"currency": "IDR",
"status": "queued"
},
{
"id": "55555556-5555-4555-8555-555555555555",
"payout_instruction_id": "22222224-2222-4222-8222-222222222222",
"amount": 5000000,
"currency": "IDR",
"status": "queued"
}
]
}
}8. Dispute a milestone
The supplier resubmitted the second milestone, but the inspection report was still missing, so the team disputes it. RekberPay operations resolves it.
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);200 OK
{
"success": true,
"data": {
"milestone_id": "11111115-1111-4111-8111-111111111111",
"status": "disputed",
"deal_status": "disputed"
}
}9. Read the deal
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);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
}
]
}
],
"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": 0,
"approvals_by_people": 2,
"revisions": 1
}
}
}Lists are cut to the first 1 items and long text is shortened here.
funded_amount and released_amount are recomputed from the milestones. Keep your ERP in sync by polling events or with webhooks.