Sign in

Send a file and read it

View as Markdown

Send a PDF, an image or an e-invoice, wait until Ocost has read it, then read its documents. One file can hold several documents. A scan of three invoices is one file with three documents.

Send the file

Post the file as multipart/form-data to POST /public/v1/files. The key needs the scope documents:write.

Shell
curl -sS https://api.ocost.io/public/v1/files \
  -H "Authorization: Bearer $OCOST_KEY" \
  -F "file=@scan.pdf" -F "name=scan.pdf" -F "external_ref=account.move,42"
FieldMeaning
fileRequired. A PDF, an image (JPEG, PNG, TIFF, WebP) or an e-invoice's XML (XRechnung, UBL, CII), up to 25 MB. Archives aren't read.
nameThe file name. Without it, Ocost takes the upload's.
external_refYour own id, up to 200 characters, given back with the file.

A new file answers 202 Accepted with "status": "received", and reading starts at once:

JSON
{ "id": "01a1b08c-6e3a-7562-8f4a-7924722667b1", "name": "scan.pdf", "status": "received", "external_ref": "account.move,42", "documents": [] }

Every answer carries X-Ocost-Documents-Left, the documents your plan has left this month. Limits and errors lists the refusals.

Send it again safely

Ocost knows a file by the SHA-256 of its bytes, not by its name. The same bytes again answer 200 OK with that file as it is now. Ocost doesn't read or count it again, and the file keeps its first external_ref. So after a timeout, send the file again.

Wait until it is read

Ask for the file with GET /public/v1/files/{file_id}, scope documents:read. Poll every 2 to 3 seconds for half a minute, then every 10 to 30 seconds. Most PDFs take under a minute, long ones a few.

Python
def wait_for(session, file_id, timeout=300):
    started = time.monotonic()
    while time.monotonic() - started < timeout:
        file = session.get(f"{API}/files/{file_id}", timeout=15).json()
        if file["status"] not in ("received", "reading"):
            return file
        time.sleep(3 if time.monotonic() - started < 30 else 15)
    return file

The file is done when its status is read, not_read or failed. Statuses and reasons explains each. For many files at once, stay in sync instead of polling each one.

Read its documents

A read file lists its documents in documents, each with its id, kind, number and pages. GET /public/v1/files/{file_id}/document answers the first one. GET /public/v1/documents/{document_id} answers any of them.

Shell
curl -sS https://api.ocost.io/public/v1/documents/01a1b08c-7aa1-7b52-9d4e-2f1f1c0c4f11 \
  -H "Authorization: Bearer $OCOST_KEY"

The answer is the document as ocost.document.v1. The document shows a whole one and explains every field.

Get the original

GET /public/v1/files/{file_id}/content answers the bytes as you sent them.

Shell
curl -sS -o scan.pdf https://api.ocost.io/public/v1/files/$FILE_ID/content \
  -H "Authorization: Bearer $OCOST_KEY"

Delete a file

DELETE /public/v1/files/{file_id} erases the file at once and for good. That includes its documents, what was read, its search index and the original. The key needs documents:write.

Shell
curl -sS -X DELETE https://api.ocost.io/public/v1/files/$FILE_ID \
  -H "Authorization: Bearer $OCOST_KEY"

Ocost answers 204, and 404 from then on. It refuses in two cases:

AnswerWhat to do
409 urn:ocost:file:readingThe file is being read. Try again later.
409 urn:ocost:file:linkedOcost still needs the file, for a booked invoice, a bank statement, an open question or a project. detail says which. Delete it in Ocost once nothing needs it.