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/health

Health check

Example 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/health

Health check (versioned)

Example 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/expenses

List expenses

Example 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

NameInTypeDescription
pagequeryintegerPage number (1-based). Use with `page_size`.
page_sizequeryintegerRows per page (max 200). Defaults to 50 when `page` is set.
limitqueryintegerLegacy page size; prefer `page_size`.
offsetqueryintegerLegacy offset; prefer `page` and `page_size`.
qquerystring—
siteIdquerystring—
categoryquerystring—
dateFromquerystring—
dateToquerystring—
minAmountqueryinteger—
maxAmountqueryinteger—

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/expenses

Create an expense

Example 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

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

NameInTypeDescription
id*pathstring—

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

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

NameInTypeDescription
id*pathstring—

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/receipts

List receipts

Example 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

NameInTypeDescription
pagequeryintegerPage number (1-based). Use with `page_size`.
page_sizequeryintegerRows per page (max 200). Defaults to 50 when `page` is set.
limitqueryintegerLegacy page size; prefer `page_size`.
offsetqueryintegerLegacy offset; prefer `page` and `page_size`.
qquerystring—
siteIdquerystring—
confirmedquerystring—
processingStatusquerystring—
awaitingReviewquerystring—

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/receipts

Upload a receipt and extract data

Example 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

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

NameInTypeDescription
id*pathstring—

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

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

NameInTypeDescription
id*pathstring—

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}/file

Download the original receipt file

Example 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

NameInTypeDescription
id*pathstring—

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}/extract

Re-run receipt extraction

Example 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

NameInTypeDescription
id*pathstring—

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}/confirm

Confirm a receipt and create an expense

Example 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

NameInTypeDescription
id*pathstring—

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/audit

Expense audit trail

Example 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