Extract API Română

Fintray Extract — integration guide

Send an invoice your customer received, as a photo or PDF, and get its data back as structured JSON. Every endpoint, field and event is described in the API reference, built from openapi.json (OpenAPI 3.1). Limits, the versioning policy and the history of changes are in Limits and versions and the changelog.

1. Keys

You get two keys:

We issue keys on request; a sandbox key is yours before any contract. GET /v1/whoami checks a key:

curl https://api.fintray.ro/v1/whoami -H "Authorization: Bearer ftk_test_…"
# {"tenant_id": "ten_…", "key_id": "key_…", "mode": "test"}

The key also works in an X-API-Key header. A wrong, revoked or expired key always gets the same 401.

2. Submitting a document

POST /v1/documents:

curl https://api.fintray.ro/v1/documents \
  -H "Authorization: Bearer ftk_live_…" \
  -H "Idempotency-Key: invoice-8841" \
  -F [email protected] \
  -F external_id=invoice-8841 \
  -F 'metadata={"client_id": "c-102"}' \
  -F buyer_vat=RO12345678 \
  -F 'buyer_name=Client SRL'
Field What it does
file PDF, JPEG, PNG, GIF or WebP; at most 14 MiB and 50 pages
external_id your reference, at most 128 characters. Not unique; you can filter on it.
metadata up to 20 scalar values, returned unchanged
buyer_vat required. The VAT number of the company that received the invoice, i.e. the customer whose account you upload it to. A Romanian CUI, with or without RO, must pass its check digit; for another EU company send the VAT number with its country prefix. It keeps the model from mistaking the vendor for the buyer, and the response tells you whether the invoice really is this customer's.
buyer_name optional, at most 200 characters. The same company's name.
mode async (the default, answers 202) or sync (waits up to 30 s, then 202)
retention standard or zero (see section 6)
webhook_url https://… for this document. Otherwise the account's URL is used.

Or a JSON body: {"file_base64": "...", "filename": "invoice.pdf", "buyer_vat": "RO12345678", "external_id": "..."}. A complete example, in Python: examples/submit.py.

Idempotency-Key. The same key with the same request returns the same document, with an Idempotent-Replayed: true header. The same key with a different request gets 409. Keys are kept 24 hours.

3. The result

GET /v1/documents/{id}:

curl https://api.fintray.ro/v1/documents/doc_… -H "Authorization: Bearer ftk_live_…"

status goes queued → processing → succeeded or failed. On succeeded the response carries:

A high field can be taken as is: on our evaluation corpus it was correct 99.6% of the time. Show a low field to your user.

GET /v1/documents?external_id=invoice-8841 lists your documents, newest first.

4. Webhooks

When a document finishes, we POST to the document's webhook_url or, if it has none, to the account's URL (we set it for you, on request). The event is document.succeeded or document.failed:

{"type": "document.succeeded", "created_at": "…",
 "data": {"id": "doc_…", "object": "document", "external_id": "invoice-8841",
          "status": "succeeded", "url": "/v1/documents/doc_…"}}

The event carries no extraction; fetch it with GET, with your key. On document.failed, data.error.code says why. Verify the signature in the Fintray-Signature header with the whsec_… secret we gave you; the example is examples/verify_webhook.py. Fintray-Event-Id stays the same on every retry of one event, so deduplicate on it.

Anything other than a 2xx is retried for 24 hours. Ask for a lost event again with POST /v1/documents/{id}/webhook.

5. Errors

Every error is {"error": {"code": "...", "message": "..."}}. The codes are in Error codes.

6. Retention and deletion

retention The file The extraction
standard deleted 24 h after processing deleted after 30 days
zero deleted as soon as it is processed deleted on the first successful GET, 24 h at most

DELETE /v1/documents/{id} erases the document: the file, the extraction and everything describing it. Only its status, page count and dates remain, for billing.

7. Versions, limits, usage