Get started

Quickstart

Get an API key, read your books and add a receipt in about five minutes.

This guide takes you from nothing to a working integration: you'll read recent entries, add a receipt, and check on it while Oatmilk reads it. You need an Oatmilk account in a company, and a terminal.

1. Get an API key

Open Developers › API keys in Oatmilk and choose Create key. Give it a name you'll recognise later, keep Read and Write for accounting, and copy the key. You only see it once.

Keys start with oat_live_ in production and oat_test_ in staging. Keep yours on your own server, never in browser code or a public repository, and pass it to your code as an environment variable:

Shell
export OATMILK_API_KEY="oat_live_…"

2. Make your first request

Every action has its own address under /api/v1/accounting/. Read actions accept GET with query parameters, so this lists your five most recent entries:

curl -G https://app.getoatmilk.com/api/v1/accounting/entries.list \
  -H "Authorization: Bearer $OATMILK_API_KEY" \
  --data-urlencode 'limit=5'

3. Read the response

A successful response wraps the result in data. Amounts are whole numbers of cents written as strings, so "4520" is $45.20.

entries.list response
{
  "data": [
    {
      "id": "7f0f6c1e-1c1f-4b5e-9c8d-2f5e8e3c1a10",
      "organization_id": "org_synthetic",
      "submission_id": "4d5e6f7a-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
      "submitted_by": "user_synthetic",
      "merchant": "Synthetic Office Supply",
      "date": "2026-09-18",
      "currency": "CAD",
      "amount_minor": "4520",
      "tax_minor": "520",
      "category_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "payment_account_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
      "type": "expense",
      "status": "reviewed",
      "notes": "",
      "revision": 3,
      "human_corrected": false,
      "autopilot_state": "done",
      "tags": []
    }
  ]
}

Every response also carries an X-Request-Id header. Keep it with your logs: it's the fastest way for us to find a request if you need help.

4. Add a receipt

Changes use POST with a JSON body. uploads.inline adds a receipt of up to 2 MB in one call: send its name, type and bytes as base64. The Idempotency-Key header makes the request safe to retry: send the same key again and the receipt is only added once.

curl https://app.getoatmilk.com/api/v1/accounting/uploads.inline \
  -H "Authorization: Bearer $OATMILK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "filename": "receipt.jpg",
  "mimeType": "image/jpeg",
  "contentBase64": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAA=="
}'

Oatmilk keeps the original file and starts reading it in the background. The response tells you which job to follow:

uploads.inline response
{
  "data": {
    "submissionId": "4d5e6f7a-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
    "jobId": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
    "generation": 1,
    "status": "queued",
    "sizeBytes": 182734
  }
}

5. Check on the job

Reading a receipt takes a few seconds. Ask for the job until its status is completed or failed. A queued or processing job is still working, not finished.

curl -G https://app.getoatmilk.com/api/v1/accounting/jobs.get \
  -H "Authorization: Bearer $OATMILK_API_KEY" \
  --data-urlencode 'jobId=2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e'
jobs.get response
{
  "data": {
    "id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
    "submission_id": "4d5e6f7a-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
    "status": "completed",
    "stage": "match",
    "generation": 1,
    "attempts": 1,
    "error_code": null,
    "next_attempt_at": "2026-09-30T14:00:05Z"
  }
}

That's it: the receipt is in the books, matched to its bank transaction when Oatmilk finds one.

Next steps