Sign in

Stay in sync

View as Markdown

Collect every file that changed since you last looked, instead of polling each file. GET /public/v1/files lists files by their last change, oldest first. A cursor tells Ocost where you stopped. The key needs the scope documents:read.

Ask for the first page

Pass updated_since to start at a time. Leave it out to start from the first file.

Shell
curl -sS -G https://api.ocost.io/public/v1/files \
  -H "Authorization: Bearer $OCOST_KEY" \
  --data-urlencode "updated_since=2026-11-09T08:00:00Z" \
  --data-urlencode "limit=100"

The answer holds the files, the cursor and whether more follow:

JSON
{
  "files": [
    {
      "id": "01a1b08c-6e3a-7562-8f4a-7924722667b1",
      "name": "invoice.pdf",
      "status": "read",
      "external_ref": "account.move,42",
      "documents": [
        { "id": "01a1b08c-7aa1-7b52-9d4e-2f1f1c0c4f11", "kind": "invoice", "number": "RE-2026-1187", "pages": [1] }
      ],
      "updated_at": "2026-11-09T08:02:41Z"
    }
  ],
  "next_cursor": "MjAyNi0xMS0wOVQwODowMjo0MVo",
  "more": false
}

A page holds up to limit files, 1 to 100, 50 by default. A file comes once for each change.

Follow the cursor

While more is true, ask again at once with cursor set to next_cursor. When more is false, keep the last next_cursor. Ask with it on your next poll, and you get only what changed since.

def changed_since(session, cursor=None):
    """Every file changed since the cursor kept from the last poll (None: all)."""
    files = []
    while True:
        params = {"limit": 100, **({"cursor": cursor} if cursor else {})}
        page = session.get(f"{API}/files", params=params, timeout=15).json()
        files += page["files"]
        cursor = page["next_cursor"] or cursor
        if not page["more"]:
            return files, cursor  # keep the cursor for the next poll

Only the finished files

Ask with status to list only files in those statuses. Repeat it for several.

Shell
curl -sS -G https://api.ocost.io/public/v1/files \
  -H "Authorization: Bearer $OCOST_KEY" \
  --data-urlencode "cursor=$CURSOR" \
  --data-urlencode "status=read" --data-urlencode "status=not_read" \
  --data-urlencode "status=failed"

status filters after paging. A page can therefore hold fewer files than limit, or none. Follow more, not the count.

Then handle each file by its status. Statuses and reasons says what each one means.