Skip to content

Uploads API ​

Files are uploaded first and then referenced by URL: an escrow attachment_url, a chat attachment_url or dispute evidence_urls. Every upload goes through the same checks:

  • The real type is read from the file's first bytes. It must match the declared type and the file extension, and be allowed for the upload type.
  • Images are decoded and saved again, which removes EXIF data such as GPS location. Images above about 40 megapixels, or wider or taller than 12,000 pixels, are refused.
  • PDFs are cleaned: scripts, automatic actions and attached files are removed, and only the cleaned copy is stored. A PDF locked with a password, a damaged PDF, or one with too many pages is refused.
  • The file gets a name chosen by RekberPay and a SHA-256 hash, and is recorded against your account. Only you can attach it, and only once, to an escrow, a chat message or a dispute.

Uploaded files are not public. API answers return them as short-lived signed links (with exp, sig and usually u query parameters). A link is bound to the person it was issued to, and the file server checks on every fetch that they may still see the file. Use the link as returned and fetch the parent resource again for a fresh one.

Endpoints ​

MethodPathEndpointAuth
GET/v1/upload/configGet the upload base URLNone
POST/v1/upload/:typeUpload a fileKey or session

Get the upload base URL ​

GET/v1/upload/config

The base URL that uploaded files are served from.

  • Auth: None. Anyone can call it.
  • Rate limit: 100 requests a minute per IP address.
  • Idempotency: Not needed. Reading has no side effects.

Example request ​

bash
curl https://api.rekberpay.com/v1/upload/config
js
const res = await fetch('https://api.rekberpay.com/v1/upload/config', {
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/upload/config",
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/upload/config');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "base_url": "https://api.rekberpay.com/uploads"
    }
}

Errors ​

StatusCodeCause
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Upload a file ​

POST/v1/upload/:type

Upload one file as multipart/form-data in a field named file. The answer gives the URL to use in other calls.

TypeUseMax sizeAllowed types
escrowEscrow attachments and dispute evidence10 MBJPEG, PNG, WebP, PDF
chatChat attachments5 MBJPEG, PNG, WebP, PDF
kycIdentity documents, for the RekberPay app10 MBJPEG, PNG, WebP
  • Auth: A personal API key (Authorization: Bearer <key>) or a session cookie with X-CSRF-Token on writes.
  • Rate limit: 40 uploads per 10 minutes per user, on top of the shared limits. There is also a daily quota per user: 300 a day for each of escrow and chat, 30 for kyc, and 400 in total.
  • Idempotency: Send an Idempotency-Key header (UUID v4). A retry with the same key returns the first answer with Idempotent-Replay: true.
  • Language: Error messages are in Indonesian. Send X-RekberPay-Lang: en for English. Every upload error also carries message_id (Indonesian) and message_en (English).

Path parameters ​

NameTypeDescription
typestringescrow, chat or kyc

Body (multipart/form-data) ​

NameTypeRequiredDescription
filefileYesThe file. One per request

Example request ​

bash
curl -X POST https://api.rekberpay.com/v1/upload/escrow \
  -H "Authorization: Bearer $REKBERPAY_API_KEY" \
  -F "[email protected];type=image/png"
js
import { readFile } from 'node:fs/promises';

const form = new FormData();
form.append('file', new Blob([await readFile('receipt.png')], { type: 'image/png' }), 'receipt.png');

const res = await fetch('https://api.rekberpay.com/v1/upload/escrow', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_API_KEY}`,
    },
    body: form,
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/upload/escrow",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_API_KEY']}",
    },
    files={"file": ("receipt.png", open("receipt.png", "rb"), "image/png")},
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/upload/escrow');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_API_KEY'),
    ],
    CURLOPT_POSTFIELDS => ['file' => new CURLFile('receipt.png', 'image/png')],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "filename": "1791901398283_862e8cf475cf7693efed4855a91995d0_7e10e097a2087a40e3e8e3b659df3f65.png",
    "upload_id": "1791901398283_862e8cf475cf7693efed4855a91995d0_7e10e097a2087a40e3e8e3b659df3f65.png",
    "url": "https://api.rekberpay.com/uploads/escrow/1791901398283_862e8cf475cf7693efed4855a91995d0_7e10e097a2087a40e3e8e3b659df3f65.png",
    "type": "escrow",
    "mime": "image/png",
    "size": 48213,
    "sha256": "3f1d2c0b9e8a7f6e5d4c3b2a1908f7e6d5c4b3a29180f7e6d5c4b3a291807f6e"
}
FieldTypeDescription
urlstringThe URL to send as attachment_url or in evidence_urls. Store it as returned
filename, upload_idstringThe name RekberPay gave the file
mimestringThe type found in the file, which can differ from what you declared only in name (a PNG stays a PNG)
sizeintegerBytes stored, after images are saved again or PDFs cleaned
sha256stringHash of the stored bytes

Errors ​

StatusCodeCause
400INVALID_TYPEtype is not escrow, chat or kyc
400NO_FILENo file in a field named file, more than one file, or a broken multipart body
400FILE_EMPTYThe file has no bytes
413FILE_TOO_LARGEThe file is over the size limit for this type
400TYPE_NOT_ALLOWEDThe content is not JPEG, PNG, WebP or PDF, or that kind is not allowed for this type
400TYPE_MISMATCHThe content does not match the declared type or the file extension
400POLYGLOTThe file hides another format inside it
400IMAGE_TOO_LARGEThe image has too many pixels
400IMAGE_DECODE_FAILEDThe image is damaged or not a real image
400PDF_PASSWORD_PROTECTEDThe PDF is locked with a password. Print it to a new PDF or upload a photo of the page
400PDF_DAMAGEDThe PDF cannot be opened
400PDF_TOO_MANY_PAGESThe PDF has more pages than allowed
400PDF_UNSUPPORTED_ENCODING, PDF_ACTIVE_CONTENT, PDF_EMBEDDED_FILEThe PDF could not be checked, or still had scripts or attached files after cleaning. Save it again as a PDF
429QUOTA_EXCEEDEDThe daily upload quota is used up
500STORAGE_FAILEDThe file was not stored. Try again
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or API key, the session expired, or the key is wrong or revoked
401WORKSPACE_KEY_ONLYA workspace key (rpe_...) was sent. Those only work under /v1/enterprise
403CSRF_FAILEDSession call without the matching X-CSRF-Token header
403ACCOUNT_INACTIVE, SUSPENDEDThe account is closed, being deleted or suspended
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

A PDF_PASSWORD_PROTECTED answer:

400 Bad Request

json
{
    "success": false,
    "error": {
        "code": "PDF_PASSWORD_PROTECTED",
        "message": "PDF ini dikunci dengan kata sandi. Buka file-nya, pilih Cetak lalu Simpan sebagai PDF, lalu unggah salinan itu. Bisa juga unggah foto atau tangkapan layar halamannya.",
        "message_id": "PDF ini dikunci dengan kata sandi. Buka file-nya, pilih Cetak lalu Simpan sebagai PDF, lalu unggah salinan itu. Bisa juga unggah foto atau tangkapan layar halamannya.",
        "message_en": "This PDF is locked with a password. Open it, choose Print, then Save as PDF, and upload that copy. You can also upload a photo or screenshot of the page."
    }
}

Attaching an upload ​

When you send an upload's URL to another call (an escrow attachment_url, a chat attachment_url or dispute evidence_urls), that call can refuse it:

StatusCodeCause
400UPLOAD_INVALID_REFThe value is not a RekberPay upload URL
400UPLOAD_NOT_FOUNDNo such upload. Upload the file again
400UPLOAD_NOT_OWNEDSomeone else uploaded this file. Upload your own copy
400UPLOAD_WRONG_PURPOSEThe upload type does not fit here, for example a kyc file in a chat
409UPLOAD_ALREADY_BOUNDThe file is already attached somewhere else
409UPLOAD_QUOTAThis escrow or dispute already has as many attachments as allowed

Escrow, payment services and B2B deals for Indonesian businesses.