Skip to content

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.

bash
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"
      }
    ]
  }'
js
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();
python
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
<?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

json
{
    "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:

bash
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."
  }'
js
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();
python
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
<?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:

bash
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"
  }'
js
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();
python
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
<?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:

bash
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"
  }'
js
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();
python
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
<?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.

bash
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 '{}'
js
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();
python
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
<?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

json
{
    "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

json
{
    "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.

bash
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"
  }'
js
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();
python
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
<?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

json
{
    "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:

bash
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"
  }'
js
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();
python
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
<?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

json
{
    "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:

bash
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": []
  }'
js
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();
python
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
<?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

json
{
    "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:

bash
curl "https://api.rekberpay.com/v1/enterprise/approvals?limit=2" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
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();
python
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
<?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

json
{
    "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

json
{
    "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:

bash
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."
  }'
js
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();
python
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
<?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

json
{
    "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

json
{
    "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.

bash
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
      }
    ]
  }'
js
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();
python
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
<?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

json
{
    "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.

bash
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."
  }'
js
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();
python
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
<?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

json
{
    "success": true,
    "data": {
        "milestone_id": "11111115-1111-4111-8111-111111111111",
        "status": "disputed",
        "deal_status": "disputed"
    }
}

9. Read the deal ​

bash
curl https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
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();
python
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
<?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

json
{
    "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.

Next steps ​

Escrow, payment services and B2B deals for Indonesian businesses.