Introduction
Expense API
Overview
Organisation-scoped expenses and receipt capture. siteId is an opaque id from the calling site. This service does not own locations, directory projections, or access roles. Amounts are integers in minor currency units (pence for GBP).
Integration guide: Expense service docs
OpenAPI v0.1.0 · source openapi/expense.json
OpenAPI 3.0.0 · v0.1.0 · 14 operations
Operations
System
Liveness and generated documentation.
get/healthHealth check
/healthExample request
curl -sS \
-X GET \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
"https://api.mayawealth.co.uk/expense/health"Unauthenticated liveness probe. Does not verify D1, R2, or Workers AI.
Operation ID: getHealth
Responses
- 200
Service is running
application/json
get/v1/healthHealth check (versioned)
/v1/healthExample request
curl -sS \
-X GET \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
"https://api.mayawealth.co.uk/expense/v1/health"Unauthenticated liveness probe. Does not verify D1, R2, or Workers AI.
Operation ID: getHealthV1
Responses
- 200
Service is running
application/json
Expenses
Spend records for any site.
get/v1/expensesList expenses
/v1/expensesExample request
curl -sS \
-X GET \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
"https://api.mayawealth.co.uk/expense/v1/expenses"Operation ID: listExpenses
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| page | query | integer | Page number (1-based). Use with `page_size`. |
| page_size | query | integer | Rows per page (max 200). Defaults to 50 when `page` is set. |
| limit | query | integer | Legacy page size; prefer `page_size`. |
| offset | query | integer | Legacy offset; prefer `page` and `page_size`. |
| q | query | string | — |
| siteId | query | string | — |
| category | query | string | — |
| dateFrom | query | string | — |
| dateTo | query | string | — |
| minAmount | query | integer | — |
| maxAmount | query | integer | — |
Responses
- 200
Expenses
application/json
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json
post/v1/expensesCreate an expense
/v1/expensesExample request
curl -sS \
-X POST \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
"https://api.mayawealth.co.uk/expense/v1/expenses"Operation ID: createExpense
Request body(required)
application/jsonExpenseCreate
Responses
- 201
Created expense
application/json
- 400
Malformed request. `error.code` is typically INVALID_REQUEST.
application/json
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 404
The requested resource was not found. `error.code` is a resource-specific *_NOT_FOUND value.
application/json
- 409
The change conflicts with current state. `error.code`: CONFLICT or RECEIPT_ALREADY_CONFIRMED.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json
get/v1/expenses/{id}Get an expense
/v1/expenses/{id}Example request
curl -sS \
-X GET \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
"https://api.mayawealth.co.uk/expense/v1/expenses/example-id"Operation ID: getExpense
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string | — |
Responses
- 200
Expense
application/json
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 404
The requested resource was not found. `error.code` is a resource-specific *_NOT_FOUND value.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json
patch/v1/expenses/{id}Update an expense
/v1/expenses/{id}Example request
curl -sS \
-X PATCH \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
"https://api.mayawealth.co.uk/expense/v1/expenses/example-id"Operation ID: updateExpense
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string | — |
Request body(required)
application/jsonExpenseUpdate
Responses
- 200
Updated expense
application/json
- 400
Malformed request. `error.code` is typically INVALID_REQUEST.
application/json
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 404
The requested resource was not found. `error.code` is a resource-specific *_NOT_FOUND value.
application/json
- 409
The change conflicts with current state. `error.code`: CONFLICT or RECEIPT_ALREADY_CONFIRMED.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json
Receipts
Uploaded receipt images, AI extraction, and confirmation.
get/v1/receiptsList receipts
/v1/receiptsExample request
curl -sS \
-X GET \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
"https://api.mayawealth.co.uk/expense/v1/receipts"Operation ID: listReceipts
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| page | query | integer | Page number (1-based). Use with `page_size`. |
| page_size | query | integer | Rows per page (max 200). Defaults to 50 when `page` is set. |
| limit | query | integer | Legacy page size; prefer `page_size`. |
| offset | query | integer | Legacy offset; prefer `page` and `page_size`. |
| q | query | string | — |
| siteId | query | string | — |
| confirmed | query | string | — |
| processingStatus | query | string | — |
| awaitingReview | query | string | — |
Responses
- 200
Receipts
application/json
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json
post/v1/receiptsUpload a receipt and extract data
/v1/receiptsExample request
curl -sS \
-X POST \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
"https://api.mayawealth.co.uk/expense/v1/receipts"Stores the original JPEG, PNG, or WebP file in R2, runs Workers AI extraction, and returns a draft. Confirming is a separate step.
Operation ID: uploadReceipt
Request body(required)
multipart/form-dataReceiptUpload
Responses
- 201
Receipt draft
application/json
- 400
Malformed request. `error.code` is typically INVALID_REQUEST.
application/json
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 404
The requested resource was not found. `error.code` is a resource-specific *_NOT_FOUND value.
application/json
- 409
The change conflicts with current state. `error.code`: CONFLICT or RECEIPT_ALREADY_CONFIRMED.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json
get/v1/receipts/{id}Get a receipt draft
/v1/receipts/{id}Example request
curl -sS \
-X GET \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
"https://api.mayawealth.co.uk/expense/v1/receipts/example-id"Operation ID: getReceipt
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string | — |
Responses
- 200
Receipt
application/json
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 404
The requested resource was not found. `error.code` is a resource-specific *_NOT_FOUND value.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json
patch/v1/receipts/{id}Correct extracted receipt values
/v1/receipts/{id}Example request
curl -sS \
-X PATCH \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
"https://api.mayawealth.co.uk/expense/v1/receipts/example-id"Operation ID: updateReceipt
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string | — |
Request body(required)
application/jsonReceiptUpdate
Responses
- 200
Updated receipt
application/json
- 400
Malformed request. `error.code` is typically INVALID_REQUEST.
application/json
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 404
The requested resource was not found. `error.code` is a resource-specific *_NOT_FOUND value.
application/json
- 409
The change conflicts with current state. `error.code`: CONFLICT or RECEIPT_ALREADY_CONFIRMED.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json
get/v1/receipts/{id}/fileDownload the original receipt file
/v1/receipts/{id}/fileExample request
curl -sS \
-X GET \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
"https://api.mayawealth.co.uk/expense/v1/receipts/example-id/file"Operation ID: getReceiptFile
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string | — |
Responses
- 200
Original file
application/octet-stream
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 404
The requested resource was not found. `error.code` is a resource-specific *_NOT_FOUND value.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json
post/v1/receipts/{id}/extractRe-run receipt extraction
/v1/receipts/{id}/extractExample request
curl -sS \
-X POST \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
"https://api.mayawealth.co.uk/expense/v1/receipts/example-id/extract"Operation ID: extractReceipt
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string | — |
Responses
- 200
Receipt with extraction
application/json
- 400
Malformed request. `error.code` is typically INVALID_REQUEST.
application/json
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 404
The requested resource was not found. `error.code` is a resource-specific *_NOT_FOUND value.
application/json
- 409
The change conflicts with current state. `error.code`: CONFLICT or RECEIPT_ALREADY_CONFIRMED.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json
post/v1/receipts/{id}/confirmConfirm a receipt and create an expense
/v1/receipts/{id}/confirmExample request
curl -sS \
-X POST \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
"https://api.mayawealth.co.uk/expense/v1/receipts/example-id/confirm"Operation ID: confirmReceipt
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string | — |
Request body
application/jsonReceiptConfirm
Responses
- 201
Confirmed receipt and expense
application/json
- 400
Malformed request. `error.code` is typically INVALID_REQUEST.
application/json
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 404
The requested resource was not found. `error.code` is a resource-specific *_NOT_FOUND value.
application/json
- 409
The change conflicts with current state. `error.code`: CONFLICT or RECEIPT_ALREADY_CONFIRMED.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json
Audit
Change log for expenses and receipts in this organisation.
get/v1/auditExpense audit trail
/v1/auditExample request
curl -sS \
-X GET \
-H "Accept: application/json" \
-H "Authorization: Bearer <jwt>" \
"https://api.mayawealth.co.uk/expense/v1/audit"Operation ID: listExpenseAudit
Responses
- 200
Audit events
application/json
- 401
Missing or invalid JWT. `error.code`: UNAUTHORIZED. Send `Authorization: Bearer <jwt>`.
application/json
- 403
Authenticated but not permitted. `error.code`: FORBIDDEN. Actor types: admin, area-manager, manager, member.
application/json
- 422
Validation failed. `error.code`: VALIDATION_ERROR. Typical cause: organisation not created yet or invalid body.
application/json
- 429
Rate limit exceeded. `error.code`: RATE_LIMITED.
application/json
- 500
Unexpected server error. `error.code`: INTERNAL.
application/json