Enterprise documents and notes
A document is a record that points at a file: the file itself lives at any https URL or in the RekberPay upload store. Documents back milestone evidence. Notes are the team's contact log on a deal (calls, emails, promises), plain text, newest first. Do not put bank numbers, passwords or other secrets in a note. Auth, roles, the workspace header and the shared limits are explained in Enterprise overview.
Endpoints
| Method | Path | Endpoint | Auth |
|---|---|---|---|
GET | /v1/enterprise/documents | List documents | viewer |
POST | /v1/enterprise/documents | Add a document | operator |
DELETE | /v1/enterprise/documents/:id | Delete a document | operator |
GET | /v1/enterprise/deals/:id/notes | List notes | viewer |
POST | /v1/enterprise/deals/:id/notes | Add a note | operator |
DELETE | /v1/enterprise/deals/:id/notes/:nid | Delete a note | operator |
List documents
Documents newest first.
- 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 |
|---|---|---|---|
deal_id | UUID | No | Only this deal |
milestone_id | UUID | No | Only this milestone |
document_type | string | No | One document type |
page | integer | No | Default 1 |
limit | integer | No | 1 to 50. Default 20 |
Example request
curl "https://api.rekberpay.com/v1/enterprise/documents?deal_id=eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee" \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"const res = await fetch('https://api.rekberpay.com/v1/enterprise/documents?deal_id=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/documents?deal_id=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/documents?deal_id=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": [
{
"id": "88888885-8888-4888-8888-888888888888",
"deal_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"deal_reference": "DEAL-2026-0009",
"milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"milestone_title": "Tooling and proof approval",
"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": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"created_at": "2026-10-13T14:33:16.424Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 1,
"total_pages": 1
}
}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 |
Add a document
Registers a file. Link it to a milestone to use it as evidence; the deal is taken from the milestone.
- 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: Not supported. A retry runs the call again.
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
title | string | Yes | 2 to 180 characters |
document_type | string | Yes | contract, invoice, purchase_order, shipping, inspection, payout or other |
file_url | string | Yes | An https URL, or a file you uploaded to the RekberPay store. At most 1,000 characters |
checksum_sha256 | string | No | 64 lowercase hex characters |
deal_id | UUID | No | A deal of the workspace |
milestone_id | UUID | No | A milestone of the workspace |
visibility | string | No | account (default) or counterparty |
Example request
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);Example response
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"
}
}Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | A field failed validation |
400 | FILE_NOT_OWNED | A RekberPay upload that you did not upload, or that belongs to an escrow |
404 | NOT_FOUND | The deal or milestone is not in this 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 |
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 |
Delete a document
Deletes a document record. Evidence on an approved or released milestone is kept.
- 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: Not supported. A retry runs the call again.
Path parameters
| Name | Type | Description |
|---|---|---|
id | UUID | The document ID |
Example request
curl -X DELETE https://api.rekberpay.com/v1/enterprise/documents/eeeeeee6-eeee-4eee-8eee-eeeeeeeeeeee \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"const res = await fetch('https://api.rekberpay.com/v1/enterprise/documents/eeeeeee6-eeee-4eee-8eee-eeeeeeeeeeee', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
},
});
const data = await res.json();import os
import requests
resp = requests.delete(
"https://api.rekberpay.com/v1/enterprise/documents/eeeeeee6-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/documents/eeeeeee6-eeee-4eee-8eee-eeeeeeeeeeee');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
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": "eeeeeee6-eeee-4eee-8eee-eeeeeeeeeeee",
"deleted": true
}
}Errors
| Status | Code | Cause |
|---|---|---|
404 | NOT_FOUND | No such document |
409 | LOCKED | Evidence for an approved or released milestone cannot be deleted |
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 LOCKED answer:
409 Conflict
{
"success": false,
"error": {
"code": "LOCKED",
"message": "Evidence for an approved or released milestone cannot be deleted."
}
}List notes
next_before is the cursor for the next older page, null when there is nothing older. author is null when the author's account was removed.
- 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 |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | 1 to 50. Default 20 |
before | string | No | The next_before of the previous page |
Example request
curl "https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/notes?limit=5" \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/notes?limit=5', {
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/notes?limit=5",
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/notes?limit=5');
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": {
"items": [
{
"id": "bbbbbbb6-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
"deal_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"milestone_id": "11111115-1111-4111-8111-111111111111",
"milestone_title": "First 20,000 cartons",
"author": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"body": "Supplier confirmed the inspection report will follow on Monday.",
"visibility": "account",
"created_at": "2026-10-13T14:33:16.519Z"
}
],
"next_before": null
}
}Errors
| Status | Code | Cause |
|---|---|---|
404 | NOT_FOUND | No such deal 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 |
Add a note
Every member can read every note. With visibility: "rekberpay" RekberPay operations can read it too. The event feed records note.added with the visibility and length, never the text.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role:
operator,finance,adminorowner. - Rate limit: 60 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 |
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
body | string | Yes | 1 to 2,000 characters after trimming. Line breaks are kept |
milestone_id | UUID | No | A milestone of this deal |
visibility | string | No | account (default) or rekberpay |
Example request
curl -X POST https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/notes \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: aaaaaaa6-aaaa-4aaa-8aaa-aaaaaaaaaaaa" \
-H "Content-Type: application/json" \
-d '{
"body": "Supplier confirmed the inspection report will follow on Monday.",
"milestone_id": "11111115-1111-4111-8111-111111111111"
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/notes', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'aaaaaaa6-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
},
body: JSON.stringify({
body: "Supplier confirmed the inspection report will follow on Monday.",
milestone_id: "11111115-1111-4111-8111-111111111111"
}),
});
const data = await res.json();import os
import requests
resp = requests.post(
"https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/notes",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "aaaaaaa6-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
},
json={
"body": "Supplier confirmed the inspection report will follow on Monday.",
"milestone_id": "11111115-1111-4111-8111-111111111111",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/notes');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
'Idempotency-Key: aaaaaaa6-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
],
CURLOPT_POSTFIELDS => json_encode([
'body' => 'Supplier confirmed the inspection report will follow on Monday.',
'milestone_id' => '11111115-1111-4111-8111-111111111111',
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
201 Created
{
"success": true,
"data": {
"id": "bbbbbbb6-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
"deal_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
"milestone_id": "11111115-1111-4111-8111-111111111111",
"milestone_title": "First 20,000 cartons",
"author": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"body": "Supplier confirmed the inspection report will follow on Monday.",
"visibility": "account",
"created_at": "2026-10-13T14:33:16.519Z"
}
}Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | Body empty or too long |
404 | NOT_FOUND | No such deal or milestone |
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 |
Delete a note
The author can delete their own note; an admin or owner can delete any note. The text is gone for good; the note.deleted event keeps who deleted it and when.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role:
operator,finance,adminorowner. - Rate limit: 60 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 |
nid | UUID | The note ID |
Example request
curl -X DELETE https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/notes/ccccccc6-cccc-4ccc-8ccc-cccccccccccc \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: ddddddd6-dddd-4ddd-8ddd-dddddddddddd"const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/notes/ccccccc6-cccc-4ccc-8ccc-cccccccccccc', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Idempotency-Key': 'ddddddd6-dddd-4ddd-8ddd-dddddddddddd',
},
});
const data = await res.json();import os
import requests
resp = requests.delete(
"https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/notes/ccccccc6-cccc-4ccc-8ccc-cccccccccccc",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
"Idempotency-Key": "ddddddd6-dddd-4ddd-8ddd-dddddddddddd",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/notes/ccccccc6-cccc-4ccc-8ccc-cccccccccccc');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Idempotency-Key: ddddddd6-dddd-4ddd-8ddd-dddddddddddd',
],
]);
$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": "ccccccc6-cccc-4ccc-8ccc-cccccccccccc",
"deleted": true
}
}Errors
| Status | Code | Cause |
|---|---|---|
403 | FORBIDDEN | You are not the author, an admin or an owner |
404 | NOT_FOUND | No such note |
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 |