v1 Base URL  https://app.doxbox.io Try it →

Developer reference

The Doxbox API

Read, upload, tag and export your account's financial documents — programmatically. Invoices, receipts, suppliers, and files, as clean JSON over HTTPS. Authenticate with a per-account key and you're one curl away.

REST · JSON Base path /api/v1 Auth Bearer dbx_… 11 endpoints
your first call
Request
curl https://app.doxbox.io/api/v1/documents?limit=1 \
  -H "Authorization: Bearer $DOXBOX_API_KEY"
200 · application/json
{
  "data": [{
    "id": 1042,
    "documentNumber": "INV-2026-118",
    "documentDate": "2026-07-14",
    "totalAmount": 1170.0,
    "currency": "ILS",
    "supplierName": "Acme Ltd"
  }],
  "nextCursor": 1041
}

Introduction

v1

The Doxbox API gives your account programmatic access to its financial documents — list and read invoices and receipts, fetch the original files, list suppliers, read and set document tags, generate exports, and upload new documents. Everything is JSON over HTTPS, scoped to a single account.

Base URL
https://app.doxbox.io
Base path
/api/v1
Format
JSON, UTF-8
Access
Read by default; uploading and changing tags require a write key

Quickstart

From zero to your first authenticated response in under a minute.

  1. Create a key. As an account admin, in the Doxbox web app on desktop: Settings → API & integrations → Create key. Copy it — it's shown only once.
  2. Export it in your shell, then make the call:
first-call
# set once
export DOXBOX_API_KEY="dbx_..."

curl https://app.doxbox.io/api/v1/documents \
  -H "Authorization: Bearer $DOXBOX_API_KEY"

Authentication

A per-account API key, sent as a Bearer token on every request except /health.

header
Authorization: Bearer dbx_<prefix>_<secret>
!Shown once. The full key appears only at creation — Doxbox stores just a hash. Lose it? Revoke and create a new one. Revocation takes effect immediately, and a key can only ever touch its own account's data.

Keys are managed in Settings → API & integrations. The same screen lists your keys (created, last used) and lets you revoke one.

Scopes

Every key carries one of two permission levels, chosen at creation. Keys are read-only unless you opt in to write — calling a write endpoint with a read key returns 403 API_KEY_FORBIDDEN.

ScopeGrants
readRead documents, suppliers & tags, generate exports (default)
writeEverything in read, plus uploading documents and changing document tags

Conventions

The handful of rules that hold across every endpoint.

Pagination

List endpoints use cursor pagination: ?limit= (1–100, default 25) and ?cursor= (the last id from the previous page). Responses carry nextCursor — an id, or null on the last page.

Rate limiting

Requests are limited per key — default 120 / minute. Every response carries the headers below; exceeding the limit returns 429 with a Retry-After.

response headers
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1785690000

Quotas

Uploads count against your plan's monthly document limit. At the limit, POST /documents returns 403 PLAN_LIMIT_EXCEEDED and no file in the batch is ingested — all-or-nothing, so you're never left with a partial upload.

Duplicate handling

Uploads are de-duplicated by file content. Re-uploading a file Doxbox already has doesn't create a second document — the response marks it "duplicate" and returns the existing id. Uploads are safe to retry.

Errors

A JSON body with a human message and, where useful, a machine-readable code.

error shape
{ "message": "API key has expired", "code": "API_KEY_EXPIRED" }
HTTPcodeMeaning
400—Invalid request (bad parameters or body)
401API_KEY_MISSINGNo Authorization: Bearer header
401API_KEY_INVALIDUnknown key
401API_KEY_REVOKEDKey was revoked
401API_KEY_EXPIREDKey is past its expiry
403API_KEY_FORBIDDENKey lacks the required scope (e.g. write)
403PLAN_LIMIT_EXCEEDEDMonthly document quota reached
400TAG_LIMIT_PER_DOCUMENTDocument already has 10 tags
404—Not found, or not in your account
404TAG_NOT_FOUNDTag not found, or not in your account
429RATE_LIMITEDRate limit exceeded (see Retry-After)

Field reference

Enumerated values you'll see in responses.

documentTypeInvoice · Receipt · CreditInvoice · Other
paymentStatusPaid · Unpaid
currencyILS · USD · EUR · GBP · JPY · AUD
tag colorteal · salmon · green · violet · sand · slate
tag sourcemanual (a person) · rule (a supplier rule) · suggested_accepted · auto

Amount fields are numbers in the document's currency. Dates are ISO 8601 strings, and may be null when Doxbox couldn't read them from the document.

The document object

Fields marked (detail) appear only on GET /documents/{id}; the list returns a lighter subset.

FieldTypeNotes
idnumberStable document id.
documentNumberstring | nullInvoice/receipt number, as read from the document.
documentTypeenumInvoice · Receipt · CreditInvoice · Other.
documentDateISO date | nullDate on the document.
poNumber (detail)string | nullPurchase-order number.
netAmount · vat · vatRate · totalAmountnumber | nullAmounts in currency; vatRate is a percentage.
currencyenumILS · USD · EUR · GBP · JPY · AUD.
paymentStatusenumPaid · Unpaid.
paymentDate (detail)ISO date | nullWhen it was marked paid.
description (detail)string | nullFree-text note.
uploadedAt (detail)ISO datetimeWhen it was added to Doxbox.
supplierName / supplier (detail)string / objectList returns the name; the detail endpoint returns { id, name, crn }.
tagsarray[{ id, name, color, source }], in the order they were added.
images (detail)arrayPage image ids [{ id }] — fetch via /documents/{id}/file?image=.

GET/api/v1/healthno auth
no key needed

Service health and version. The one endpoint that needs no key — handy for uptime checks.

200 · ok
{ "status": "ok", "api": "v1" }
GET/api/v1/documents
uses your key ↑

List the account's documents, newest first. Cursor-paginated with limit and cursor.

QueryTypeNotes
limitinteger1–100, default 25
cursorintegerThe last document id from the previous page
tagsstringComma-separated tag ids, e.g. 7,12. Only documents carrying every listed tag (AND). Ids come from GET /tags.
request
curl "https://app.doxbox.io/api/v1/documents?limit=2" \
  -H "Authorization: Bearer $DOXBOX_API_KEY"
200 · application/json
{
  "data": [{
    "id": 1042, "documentNumber": "INV-2026-118",
    "totalAmount": 1170.0, "currency": "ILS",
    "documentType": "Invoice", "paymentStatus": "Unpaid",
    "supplierName": "Acme Ltd",
    "tags": [{ "id": 7, "name": "Tax refund", "color": "teal", "source": "manual" }]
  }],
  "nextCursor": 1041
}
GET/api/v1/documents/{id}
uses your key ↑

A single document with full metadata and the ids of its image pages. A document that isn't in your account returns 404 — no cross-account existence leak.

200 · application/json
{
  "id": 1042, "documentNumber": "INV-2026-118",
  "documentType": "Invoice",
  "netAmount": 1000.0, "vat": 170.0, "totalAmount": 1170.0,
  "currency": "ILS", "paymentStatus": "Unpaid",
  "supplier": { "id": 9, "name": "Acme Ltd", "crn": "514123456" },
  "images": [{ "id": 5001 }, { "id": 5002 }],
  "tags": [{ "id": 7, "name": "Tax refund", "color": "teal", "source": "rule" }]
}
GET/api/v1/documents/{id}/file

Returns a 302 redirect to a fresh, short-lived signed URL for the document's file — the storage bucket stays private. Optional ?image=<id> selects a specific page (defaults to the first).

follow the redirect, save the file
curl -L https://app.doxbox.io/api/v1/documents/1042/file \
  -H "Authorization: Bearer $DOXBOX_API_KEY" \
  -o invoice.pdf
POST/api/v1/documentsrequires write

Upload one or more documents. They run through the same pipeline as documents added in the app — OCR, supplier matching, de-duplication — and appear tagged as uploaded via the API. Send multipart/form-data with one or more files in the files field. Counts against your monthly quota; the batch is all-or-nothing.

request
curl -X POST https://app.doxbox.io/api/v1/documents \
  -H "Authorization: Bearer $DOXBOX_API_KEY" \
  -F "files=@invoice-july.pdf" \
  -F "files=@receipt-123.pdf"
201 · created
{
  "results": [
    { "filename": "invoice-july.pdf", "documentId": 1055, "status": "created" },
    { "filename": "receipt-123.pdf", "documentId": 980, "status": "duplicate" }
  ],
  "summary": { "total": 2, "created": 1, "duplicate": 1, "failed": 0 }
}

Per-file status is created, duplicate, or failed — a failed file carries a reason and never stops the rest of the batch.

GET/api/v1/tags
uses your key ↑

The account's tags (the labels a business puts on its documents), with how many documents carry each.

request
curl https://app.doxbox.io/api/v1/tags \
  -H "Authorization: Bearer $DOXBOX_API_KEY"
200 · application/json
{ "data": [
  { "id": 7, "name": "Tax refund", "color": "teal", "documentCount": 14 },
  { "id": 12, "name": "Project Ramat Gan", "color": "salmon", "documentCount": 3 }
] }
PUT/api/v1/documents/{id}/tagsrequires write

Replace the document's whole tag list. Send every tag id the document should carry; tags left out are removed.

The document and every tag must belong to your account (404 otherwise). A document carries at most 10 tags. Tags are created in the Doxbox app; the API attaches existing ones.

BodyTypeNotes
tagIdsnumber[]The full list, at most 10 ids. [] removes every tag.
request
curl -X PUT https://app.doxbox.io/api/v1/documents/1042/tags \
  -H "Authorization: Bearer $DOXBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tagIds": [7, 12] }'
200 · the document's tags
{ "data": [
  { "id": 7, "name": "Tax refund", "color": "teal", "source": "manual" },
  { "id": 12, "name": "Project Ramat Gan", "color": "salmon", "source": "manual" }
] }
POST/api/v1/documents/{id}/tagsrequires write

Add one tag to the document. If the document already carries it, nothing changes.

The document and every tag must belong to your account (404 otherwise). A document carries at most 10 tags. Tags are created in the Doxbox app; the API attaches existing ones.

BodyTypeNotes
tagIdnumberAn id from GET /tags
request
curl -X POST https://app.doxbox.io/api/v1/documents/1042/tags \
  -H "Authorization: Bearer $DOXBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tagId": 7 }'
200 · the document's tags
{ "data": [
  { "id": 7, "name": "Tax refund", "color": "teal", "source": "manual" },
  { "id": 12, "name": "Project Ramat Gan", "color": "salmon", "source": "manual" }
] }
DELETE/api/v1/documents/{id}/tags/{tagId}requires write

Remove one tag from the document.

The document and every tag must belong to your account (404 otherwise). A document carries at most 10 tags. Tags are created in the Doxbox app; the API attaches existing ones.

request
curl -X DELETE https://app.doxbox.io/api/v1/documents/1042/tags/12 \
  -H "Authorization: Bearer $DOXBOX_API_KEY"
200 · the document's tags
{ "data": [
  { "id": 7, "name": "Tax refund", "color": "teal", "source": "manual" }
] }
GET/api/v1/suppliers
uses your key ↑

The account's suppliers (those with documents), with per-supplier aggregates, sorted by total amount.

200 · application/json
{
  "data": [
    { "id": 8, "name": "Globex", "crn": "514000222", "documentCount": 12, "totalAmount": 18400.0 },
    { "id": 9, "name": "Acme Ltd", "crn": "514123456", "documentCount": 5, "totalAmount": 5850.0 }
  ]
}
POST/api/v1/exports

Generate an export file for a month's documents. Returns a signed download URL valid ~12 hours. Exporting via the API never changes your documents.

FieldTypeNotes
exportTypestringexcel_monthly · csv · pdf_merged · zip
scopestringall_results (default) or selected
selectedDocumentIdsnumber[]required when scope is selected
filters.yearnumbere.g. 2026
filters.monthnumber1–12
request
curl -X POST https://app.doxbox.io/api/v1/exports \
  -H "Authorization: Bearer $DOXBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "exportType": "excel_monthly", "filters": { "year": 2026, "month": 7 } }'
200 · application/json
{
  "fileName": "Acme_2026-07.xlsx",
  "fileUrl": "https://storage.googleapis.com/.../signed...",
  "totalDocuments": 17, "skippedFiles": 0, "provider": "server"
}

fileUrl is a temporary signed URL, valid for about 12 hours.


Recipes

Common end-to-end flows, ready to adapt.

Paginate through every document

paginate.js
let cursor, all = [];
do {
  const url = `https://app.doxbox.io/api/v1/documents?limit=100` + (cursor ? `&cursor=${cursor}` : "");
  const { data, nextCursor } = await (await fetch(url, { headers })).json();
  all.push(...data); cursor = nextCursor;
} while (cursor);   // nextCursor is null on the last page

Automate a monthly export

monthly-export.sh
# create an Excel export for a month and download it
url=$(curl -s -X POST https://app.doxbox.io/api/v1/exports \
  -H "Authorization: Bearer $DOXBOX_API_KEY" -H "Content-Type: application/json" \
  -d '{ "exportType": "excel_monthly", "filters": { "year": 2026, "month": 7 } }' \
  | jq -r .fileUrl)
curl -L "$url" -o export.xlsx    # signed URL, ~12h