Developers

An API built around one job: getting invoices into your books.

Upload a document and get structured, human-reviewed data back — with the general-ledger accounts already resolved against the company's own chart. Five endpoints, one API key, no SDK required.

Quickstart

Authenticate with a bearer token on every request. If you already have an account, keys are issued from Settings → API keys and shown once, at creation. The full reference, including the incremental sync loop, is at /developers/api-reference.

Send a document in

curl -X POST https://invoices.coresrp.com/ingest/invoices \
  -H "Authorization: Bearer $CI_API_KEY" \
  -H "X-Request-ID: $(uuidgen)" \
  -F "company=vbacc0107" \
  -F "file=@invoice.pdf;type=application/pdf"

A 202 means the file is stored and queued for extraction — it is your signal to move the file out of your outbound folder.

Read reviewed invoices out

curl -G https://invoices.coresrp.com/pull/invoices \
  -H "Authorization: Bearer $CI_API_KEY" \
  --data-urlencode "company=vbacc0107" \
  --data-urlencode "updated_since=2026-08-01T00:00:00Z"

Results are cursor-paginated and ordered so that concurrent edits never cause a document to be skipped or duplicated across pages.

The endpoint surface

Deliberately small. Everything an integration needs, and nothing that exposes the rest of the platform.

Endpoint Purpose Rate limit
POST /ingest/invoices Upload a PDF or scan for extraction and review. 60/min
GET /pull/companies List the companies your key can reach. 120/min
POST /pull/companies Register a company. Create-only and idempotent. 120/min
GET /pull/invoices Read reviewed invoices incrementally, with GL coding. 120/min
POST /pull/invoices/{invoice_id}/error Report that a document could not be posted. 120/min

Rate limits are keyed by source IP and request path, so the two /pull/companies verbs share one budget. Responses carry an x-request-id you should log — it lets us trace any individual call. Full request and response detail for every endpoint is in the API reference.

What you get back

Extraction is only half of it. Each invoice arrives with the debit and credit accounts already chosen for the company that owns it, so your side posts codes rather than inferring them.

Keys are organization-scoped
One key reaches every company in its organization, so each request names the company by slug. There is no per-company key — if you need hard isolation, ask for a separate organization.
Money travels as strings
Every amount, quantity and rate is a JSON string such as "1100.00". Parse them as decimals. Parsing as floats will eventually misstate a voucher by a cent.
Post from gl_mapping
Account codes are resolved server-side against each company's own chart of accounts, so you post the codes you are given rather than deriving them. Codes are editable per company — never hard-code them.
Reviewed means human-approved
The pull feed serves documents a person has checked, not raw extraction output. That is what makes the GL coding trustworthy enough to post automatically.
{
  "id": "01a02806-feea-7c33-b89f-390b59c0950f",
  "vendor_name": "LED HOUSE SARL",
  "invoice_number": "INV-88213",
  "invoice_date": "2026-08-14",
  "description": "LED Panel 60x60 40W +2 more",
  "currency": "USD",
  "subtotal": "630.63",
  "tax_total": "69.37",
  "grand_total": "700.00",
  "vat_applicable": true,
  "gl_mapping": {
    "debit_code":  "60111", "debit_name":  "Purchases of Goods Taxable VAT",
    "credit_code": "4011",  "credit_name": "Suppliers - Invoices",
    "vat_code":    "44210", "vat_name":    "Input VAT (Purchases)"
  },
  "cost_center_id": "01a03e11-7c40-7a02-9f31-6b1d0c4e2b88",
  "cost_center_name": "Boiler consumption",
  "line_items": [ /* ... */ ]
}

Three rules for a correct sync

If you poll for invoices, these are the details that decide whether your integration is reliable. They are easy to miss and expensive to discover in production.

  1. Deduplicate on invoice id

    The updated_since filter is inclusive, so the boundary invoice is re-served on every poll. Record the ids you have posted and skip them on sight, or you will double-post.

  2. Never advance past a failure

    Your watermark is the highest updated_at you successfully handled. If anything failed during a pass, leave the watermark where it was so the next poll re-pulls it.

  3. Run one poller per company

    Deduplication alone does not make two pollers safe: the second can advance the watermark past a document the first is still working on. Give each worker its own watermark, or run a single poller.

Building an integration?

We will send you the full endpoint reference and issue a sandbox key against a separate environment, so you never develop against live books.

Request developer access