Skip to content

REST API

Bearer-token-authed REST API at /api/v1/* for mobile clients, n8n, Home Assistant, anything that speaks HTTP.

Authentication

Get a token by exchanging email + password:

curl -sX POST https://pantria.your-host/api/v1/sessions \
     -d 'email=demo@homestead.local&password=password123'
# => { "token": "...", "user": { "id": 1, "name": "Demo", "email": "..." } }

Stash the token; every subsequent request adds:

Authorization: Bearer <token>

Tokens are per-user, never expire by themselves, and can be revoked from the user's account page. They identify the user; the single household this instance serves (Household.current) is the scope for every API call. The old X-Household-Id header is no longer used.

Endpoints

Catalog

Method Path Description
GET /api/v1/products List products in current household
POST /api/v1/products Create a product
GET /api/v1/products/lookup?barcode=X Look up a barcode (local → external waterfall)
GET /api/v1/stores List stores
POST /api/v1/stores Create a store
GET /api/v1/prices?product_id=X Price history for a product
POST /api/v1/prices Record a price

Inventory

Method Path Description
GET /api/v1/storage_items List storage rows
POST /api/v1/storage_items Add to storage
PATCH /api/v1/storage_items/:id Update qty / location / expires
DELETE /api/v1/storage_items/:id Remove
GET /api/v1/grocery_items List grocery rows
POST /api/v1/grocery_items Add (freeform or product-linked)
PATCH /api/v1/grocery_items/:id Update
POST /api/v1/grocery_items/scan_purchase Mark as bought by scanning EAN at the till

Receipts

Method Path Description
POST /api/v1/receipts Upload a receipt for OCR (multipart image=)
POST /api/v1/receipts?inline=1 Upload + run OCR synchronously
GET /api/v1/receipts/:id Receipt + parsed lines
POST /api/v1/receipts/:id/confirm Apply user's choices (same JSON shape as the web form)
POST /api/v1/receipts/:id/reprocess Re-run OCR

Inbound email

Method Path Description
GET /api/v1/inbound_emails List the caller's sources + per-source health
POST /api/v1/inbound_emails/poll Drain all of them
POST /api/v1/inbound_emails/:id/poll Drain one specific source

Examples

TOKEN="paste-here"

# 1. Look up a barcode (local → OpenFoodFacts → Marktguru)
curl -s "https://pantria.your-host/api/v1/products/lookup?barcode=4006381333924" \
     -H "Authorization: Bearer $TOKEN"

# 2. Upload a receipt + run OCR synchronously
curl -s "https://pantria.your-host/api/v1/receipts?inline=1" \
     -H "Authorization: Bearer $TOKEN" \
     -F image=@/path/to/receipt.jpg

# 3. Mark a needed grocery item as purchased by scanning its barcode
curl -sX POST https://pantria.your-host/api/v1/grocery_items/scan_purchase \
     -H "Authorization: Bearer $TOKEN" \
     -d 'barcode=4006381333924'

# 4. Trigger an inbound-email drain (e.g. from n8n on a cron schedule)
curl -sX POST https://pantria.your-host/api/v1/inbound_emails/poll \
     -H "Authorization: Bearer $TOKEN"

Response shape

JSON only, snake_case, application/json content-type. Errors come back as { "error": "<message>" } with an appropriate 4xx / 5xx code.

{
  "data": {
    "id": 42,
    "name": "Vollmilch 1L",
    "brand": "Bio Wiesengold",
    "barcode": "4006381333924",
    "unit": "l",
    "storage": [
      { "id": 7, "quantity": 2, "location": "fridge", "expires_on": "2026-05-13" }
    ]
  }
}

Source