API Reference

Invoices

Read permitted invoices through the Jump EHR API.

GET /invoices and GET /invoices/{id} use one authorised shared read service. A live practice key must explicitly have read_invoices; patient and payment grants do not imply invoice access and existing keys are not updated. Drafts, null and unknown statuses are excluded. No invoice write action is provided.

List accepts only patient_id, status, currency, limit (default 20, maximum 100) and offset (maximum 100000). Currency is an exact, case-sensitive native three-letter code; the API does not normalise it or assume GBP. Unsupported, empty or repeated parameters return 422. Unavailable patient filters return 404. Get accepts a UUID and no query parameters. Sort is created_at DESC NULLS LAST, id DESC, with one lookahead for has_more/next_offset. Offset pagination is not a consistent export snapshot under concurrent changes.

Both invoice and patient ownership are checked. Replacement/predecessor IDs are exposed only when the target is a visible invoice in the same practice for the same patient. Broken, draft or foreign references fail closed. This may make a legacy invoice temporarily unavailable until its reference is fixed.

Responses preserve recorded amount_cents, amount_due_cents, amount_paid_cents, amount_refunded_cents, amount_remaining_cents, amount_balance_applied_cents and amount_credited_to_balance_cents. Amounts must be safe integers in native minor units; signed values are retained and nullable values stay null. These are stored facts, not a recalculated ledger. Status/date inconsistencies are not silently repaired, and no payment, credit or refund total is inferred from incomplete data.

Native states are open, past_due, partially_paid, paid, void, uncollectible, refunded, partially_refunded and credited. Lifecycle dates and replacement links are retained. A superseded invoice can retain its original billed amount alongside its replacement: summing every row is not a correct practice billing total. This API does not provide an aggregate or net-billed figure and does not infer overdue status from the current date.

The public invoice_number follows the same helper as the product: issued Stripe number first, otherwise Xero number, otherwise null. Internal invoice IDs are not a fallback display number. Provider IDs, hosted/PDF/payment URLs, payload/metadata JSON, descriptions and sync/retry internals are not returned. Line items are not reconstructed from provider JSON.

Example: GET /invoices?patient_id=16263000-0000-4000-8000-000000000002&status=open&currency=gbp. List/get use {data, meta}; list metadata includes limit, has_more, next_offset and request_id. Successful reads use Cache-Control: no-store. First-party request attribution is preserved; analytics for this resource use the fixed /api-v1/invoices label without invoice IDs or query filters.

Payments, invoice drafts, line items, collection, voiding, refunds and credits are separate operations. No MCP permission or FHIR conformance is implied. Final hosted CI, security review and deployed PostgREST/staging acceptance remain pending while the PR is draft.

GET
/invoices

Authorization

BearerApiKey
AuthorizationBearer <token>

API key with the pk_live_ prefix. pk_test_ keys are refused (403 TEST_MODE_UNAVAILABLE) until a sandbox exists.

In: header

Query Parameters

limit?integer
Range1 <= value <= 100
Default20
offset?integer
Range0 <= value <= 100000
Default0
patient_id?string
Formatuuid
status?string

Value in

  • "open"
  • "past_due"
  • "partially_paid"
  • "paid"
  • "void"
  • "uncollectible"
  • "refunded"
  • "partially_refunded"
  • "credited"
currency?string

Exact case-sensitive native three-letter code

Match^[A-Za-z]{3}$

Response Body

application/json

curl -X GET "https://example.com/invoices"
{  "data": [    {      "id": "string",      "patient_id": "string",      "invoice_number": "string",      "status": "open",      "currency": "string",      "amount_cents": 0,      "amount_due_cents": 0,      "amount_balance_applied_cents": 0,      "amount_credited_to_balance_cents": 0,      "amount_paid_cents": 0,      "amount_refunded_cents": 0,      "amount_remaining_cents": 0,      "created_at": "string",      "updated_at": "string",      "due_date": "string",      "finalized_at": "string",      "paid_at": "string",      "voided_at": "string",      "replaced_at": "string",      "replaces_invoice_id": "string",      "replaced_by_invoice_id": "string"    }  ],  "meta": {    "limit": 20,    "next_offset": 1,    "has_more": true,    "request_id": "req_abc123"  }}
GET
/invoices/{id}

Authorization

BearerApiKey
AuthorizationBearer <token>

API key with the pk_live_ prefix. pk_test_ keys are refused (403 TEST_MODE_UNAVAILABLE) until a sandbox exists.

In: header

Path Parameters

id*string
Formatuuid

Response Body

application/json

curl -X GET "https://example.com/invoices/497f6eca-6276-4993-bfeb-53cbbbba6f08"
{  "data": {    "id": "string",    "patient_id": "string",    "invoice_number": "string",    "status": "open",    "currency": "string",    "amount_cents": 0,    "amount_due_cents": 0,    "amount_balance_applied_cents": 0,    "amount_credited_to_balance_cents": 0,    "amount_paid_cents": 0,    "amount_refunded_cents": 0,    "amount_remaining_cents": 0,    "created_at": "string",    "updated_at": "string",    "due_date": "string",    "finalized_at": "string",    "paid_at": "string",    "voided_at": "string",    "replaced_at": "string",    "replaces_invoice_id": "string",    "replaced_by_invoice_id": "string"  },  "meta": {    "request_id": "req_abc123"  }}