API Documentation

Integrate your expense management workflow with external services like N8N, Zapier, or your own applications.

Authentication

All API requests require a valid API key sent in the Authorization header. You can generate API keys from Settings → API Keys.

Authorization: Bearer lxi_your_api_key_here

Security note

API keys are hashed before storage and shown only once at creation. Treat them like passwords. If a key is compromised, revoke it immediately from Settings.

Base URL

All endpoints are relative to:

https://brilliant-hornet-477.convex.site/api/v1

Responses are JSON with CORS enabled for all origins.

Receipts

POST/api/v1/receipts/upload

Upload a receipt image and process it through the full AI pipeline (OCR + categorization). Returns the extracted expense data.

Headers

Content-Type: image/jpeg  (or image/png, application/pdf)
X-File-Name: receipt.jpg  (optional, for naming)
Authorization: Bearer lxi_...

Body

Send the file as the raw binary request body(not form-data, not base64). In N8N, use an HTTP Request node with "Send Body" enabled, body type "Raw/Binary", and pipe the file content directly. Max 20 MB.

N8N Example

HTTP Request Node:
  Method: POST
  URL: https://brilliant-hornet-477.convex.site/api/v1/receipts/upload
  Authentication: Header Auth
    Name: Authorization
    Value: Bearer lxi_your_key
  Send Headers: ON
    Content-Type: image/jpeg
    X-File-Name: receipt.jpg
  Send Body: ON
    Body Content Type: Raw
    Content Type: image/jpeg
    Body: {{ $binary.data }}  (from previous node)

Response

{
  "success": true,
  "jobId": "k17f...",
  "expenseId": "j57d...",
  "data": {
    "merchant": "Main Street Restaurant",
    "amount": 29.01,
    "currency": "USD",
    "date": "2017-04-07",
    "category": "fine-dining",
    "city": "Palo Alto",
    "country": "USA",
    ...
  }
}
GET/api/v1/receipts/status?jobId={id}

Check the processing status of a receipt upload job.

Query Parameters

  • jobId (required) — The receipt job ID returned from the upload endpoint.

Response

{
  "_id": "k17f...",
  "status": "complete",   // uploading | parsing | analyzing | complete | failed
  "expenseId": "j57d...",
  "errorMessage": null
}

Expenses

GET/api/v1/expenses

List expenses with optional filters. Returns most recent first.

Query Parameters

  • category — Filter by category (e.g. fine-dining, travel, fashion)
  • status — Filter by status (processed, pending, error)
  • from — Start date in YYYY-MM-DD format
  • to — End date in YYYY-MM-DD format
  • limit — Max results (default: 50, max: 100)

Example

GET /api/v1/expenses?category=travel&from=2026-01-01&limit=10
GET/api/v1/expenses/{id}

Get a single expense by its ID.

Response

{
  "_id": "j57d...",
  "merchant": "Main Street Restaurant",
  "amount": 29.01,
  "currency": "USD",
  "date": "2017-04-07",
  "time": "11:36",
  "category": "fine-dining",
  "city": "Palo Alto",
  "country": "USA",
  "tax": 0,
  "subtotal": 25.23,
  "paymentMethod": "DISCOVER",
  "status": "processed",
  "ocrRawText": "...",
  "tags": ["personal"]
}
POST/api/v1/expenses

Create an expense manually (without receipt upload).

Body (JSON)

{
  "merchant": "Restaurant Name",     // required
  "amount": 42.50,                   // required
  "currency": "MXN",                 // required
  "date": "2026-03-31",             // required (YYYY-MM-DD)
  "time": "14:30",                  // optional, default "00:00"
  "category": "fine-dining",        // optional, default "other"
  "city": "Mexico City",            // optional
  "country": "Mexico",              // optional
  "tax": 6.80,                      // optional, default 0
  "subtotal": 35.70,                // optional
  "paymentMethod": "Visa",          // optional
  "notes": "Business lunch",        // optional
  "tags": ["business", "deductible"] // optional
}

Response

{ "success": true, "id": "j57d..." }
PATCH/api/v1/expenses/{id}

Update fields on an existing expense. Send only the fields you want to change.

Body (JSON)

{
  "category": "travel",
  "tags": ["business", "deductible"],
  "notes": "Updated note"
}
DELETE/api/v1/expenses/{id}

Permanently delete an expense.

Response

{ "success": true }

Summary & Analytics

GET/api/v1/expenses/summary

Get aggregated expense statistics. Useful for dashboards and automated reports.

Query Parameters

  • from — Start date (YYYY-MM-DD)
  • to — End date (YYYY-MM-DD)

Response

{
  "totalExpenses": 16,
  "totalAmount": 54826.01,
  "totalTax": 4963.41,
  "byCategory": {
    "fine-dining": { "count": 5, "total": 4546.01 },
    "fashion": { "count": 4, "total": 20650.00 },
    "hotels": { "count": 3, "total": 10660.00 },
    ...
  },
  "byMonth": {
    "2026-03": { "count": 5, "total": 12847.00 },
    "2026-02": { "count": 11, "total": 41979.01 }
  },
  "byStatus": {
    "processed": 15,
    "pending": 1
  }
}

Code Examples

cURL — Upload a receipt

curl -X POST \
  https://brilliant-hornet-477.convex.site/api/v1/receipts/upload \
  -H "Authorization: Bearer lxi_your_key" \
  -H "Content-Type: image/jpeg" \
  -H "X-File-Name: receipt.jpg" \
  --data-binary @receipt.jpg

cURL — List expenses

curl -X GET \
  "https://brilliant-hornet-477.convex.site/api/v1/expenses?category=travel&from=2026-01-01" \
  -H "Authorization: Bearer lxi_your_key"

JavaScript — Create expense

const response = await fetch(
  "https://brilliant-hornet-477.convex.site/api/v1/expenses",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer lxi_your_key",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      merchant: "Oxxo",
      amount: 42.50,
      currency: "MXN",
      date: "2026-03-31",
      category: "other",
    }),
  }
);

const data = await response.json();
console.log(data.id);

Python — Get summary

import requests

response = requests.get(
    "https://brilliant-hornet-477.convex.site/api/v1/expenses/summary",
    headers={"Authorization": "Bearer lxi_your_key"},
    params={"from": "2026-01-01", "to": "2026-03-31"}
)

data = response.json()
print(f"Total: {data['totalAmount']}")
print(f"Expenses: {data['totalExpenses']}")

Error Handling

All errors return a JSON body with an error field:

{ "error": "Description of what went wrong" }
StatusMeaning
400Bad request — missing or invalid parameters
401Unauthorized — invalid or missing API key
404Resource not found
500Server error — pipeline or processing failure

LUX INTELLECT API v1 · Questions? Contact support