# Send a file and read it

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`.

```bash
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"
```

| Field | Meaning |
| --- | --- |
| `file` | Required. 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. |
| `name` | The file name. Without it, Ocost takes the upload's. |
| `external_ref` | Your 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](https://docs.ocost.io/en/limits.md) 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](https://docs.ocost.io/en/statuses.md) explains each. For many files at once, [stay in sync](https://docs.ocost.io/en/sync.md) 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.

```bash
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](https://docs.ocost.io/en/document.md) 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.

```bash
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`.

```bash
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:

| Answer | What to do |
| --- | --- |
| `409 urn:ocost:file:reading` | The file is being read. Try again later. |
| `409 urn:ocost:file:linked` | Ocost 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. |
