Skip to content

Create

POST
/expenses
curl --request POST \
--url https://example.com/expenses \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: 2489E9AD-2EE2-8E00-8EC9-32D5F69181C0' \
--data '{ "categoryId": "example", "jobId": "example", "date": "2026-04-15", "currency": "example", "amount": "example", "taxAmount": "example", "taxRateId": "example", "description": "example", "paidBy": "company", "receiptFileId": "example", "isBillable": false }'

Who can call it: Owners, Staff and Field users. Staff need the Team right Submit/edit own expenses (expenses.edit_own).

curl -X POST "https://spine.example.com/api/expenses" \
  -H "Cookie: better-auth.session_token=<session>" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" -d '{ … }'
Idempotency-Key
required
string format: uuid

A fresh UUID per action. A retry with the same key gets the first answer instead of doing it twice.

Media typeapplication/json
object
categoryId
required
string
jobId
string
nullable
date
required
string format: date
currency
required

ISO 4217 currency code.

string
amount
required

Integer minor units, as a numeric string.

string
taxAmount

Integer minor units, as a numeric string. Defaults to 0.

string
taxRateId
string
nullable
description
string
nullable
paidBy
string
default: company
Allowed values: company employee
receiptFileId

A file this person uploaded via POST /files that nothing else uses yet (D-250).

string
nullable
isBillable
boolean

Validation failed or a business rule was broken; message says what to change.

No session, or it has expired: sign in again.

The caller’s role, Team rights or ownership don’t allow it, the module is switched off, or their access was removed.

No such record in this business.

The same Idempotency-Key is still being handled, or the record conflicts with one that exists.

Too many requests: wait a minute.

Something went wrong on the server; the response carries the request id to report.