API Reference

Patient messages

Read permitted patient messages through the Jump EHR API.

GET /messages and GET /messages/{id} return patient-directed communication metadata. GET /messages/{id}/content retrieves a recorded source copy, requiring both read_messages and read_message_content on a live organisation key. Metadata grants alone never fetch bodies or subjects. Existing keys are not upgraded; no MCP capability is enabled by the route inventory.

The backing store is communications, not staff/CRM/support mailboxes. Both the communication and joined patient must belong to the key's practice, and recipient_scope must be patient. A row being about a patient does not mean it was addressed to them. Internal notifications are excluded. Channels covered are native email, sms, push and app; notes, calls and unknown channels are outside this initial contract. Draft and unknown status rows are excluded.

List accepts only patient_id, channel_type, direction, status, limit (default 20, maximum 100) and offset (maximum 100000). Empty, repeated and unsupported parameters return 422. An unavailable patient filter returns 404. Sort is created_at DESC NULLS LAST, id DESC, with one lookahead row for has_more and next_offset. Offset pagination is not a consistent export snapshot during concurrent changes. Get/content accept a UUID and no query.

Metadata includes identity, patient, channel, direction, native status and delivery code, attachment-presence flag, and creation/update/occurrence/send/ failure/read/retraction timestamps. It does not include bodies, subjects, raw addresses, telephone numbers, failure detail, provider IDs, status-history JSON, verification claims or attachment paths. A presence flag does not mean attachment bytes are retrievable through this API. Delivery codes are preserved, not treated as proof of delivery, identity verification or clinical review. Retracted rows remain identifiable in metadata while their content is unavailable.

Content returns {id, patient_id, available, content?}. A successful copy has source, format, subject and body. Outbound sent/delivered/read/opened/bounced records prefer sent_html with format: html; when absent, a recorded sent_body is returned as format: native. Only sent_subject is used. The current draft body and subject are never substituted. HTML is the stored representation, not newly sanitised HTML; clients must apply their own safe rendering policy.

Inbound received rows expose their recorded body/subject with format: native. This does not establish an authenticated sender or imply that the source is plain text. Missing copies, queued/sending/failure states and retractions report available: false with not_recorded, not_sent_or_received or retracted. Empty recorded copies remain empty. Content responses are capped at 1 MiB of UTF-8 body bytes and 4096 subject bytes; oversized records fail closed. These are response checks after database retrieval, not a pre-buffer network limit.

Example: GET /messages?patient_id=16261000-0000-4000-8000-000000000002&direction=outbound&channel_type=email. List/get use the normal {data, meta} envelope; list includes pagination meta. Content uses the same envelope around its availability result. Successful responses use Cache-Control: no-store. First-party logs retain practice/key/ request attribution; analytics for the new resource use a fixed /api-v1/messages label without message IDs or query filters. General analytics cleanup is #15496.

Sending, retries, drafts, marking read and attachment retrieval are separate operations. Recorded content can itself contain patient links or personal information, which is why access has its own grant. Final CI, security/clinical assessment and deployed PostgREST/staging acceptance remain pending until reviewed.

GET
/messages

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
channel_type?string

Value in

  • "email"
  • "sms"
  • "push"
  • "app"
direction?string

Value in

  • "inbound"
  • "outbound"
status?string

Value in

  • "queued"
  • "sending"
  • "in_progress"
  • "sent"
  • "delivered"
  • "read"
  • "opened"
  • "bounced"
  • "failed"
  • "error"
  • "received"

Response Body

application/json

curl -X GET "https://example.com/messages"
{  "data": [    {      "id": "string",      "patient_id": "string",      "channel_type": "email",      "direction": "inbound",      "status": "queued",      "delivery_status": "string",      "has_attachments": true,      "created_at": "string",      "updated_at": "string",      "occurred_at": "string",      "sent_at": "string",      "failed_at": "string",      "read_at": "string",      "voided_at": "string"    }  ],  "meta": {    "limit": 20,    "next_offset": 1,    "has_more": true,    "request_id": "req_abc123"  }}
GET
/messages/{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/messages/497f6eca-6276-4993-bfeb-53cbbbba6f08"
{  "data": {    "id": "string",    "patient_id": "string",    "channel_type": "email",    "direction": "inbound",    "status": "queued",    "delivery_status": "string",    "has_attachments": true,    "created_at": "string",    "updated_at": "string",    "occurred_at": "string",    "sent_at": "string",    "failed_at": "string",    "read_at": "string",    "voided_at": "string"  },  "meta": {    "request_id": "req_abc123"  }}
GET
/messages/{id}/content

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/messages/497f6eca-6276-4993-bfeb-53cbbbba6f08/content"
{  "data": {    "id": "string",    "patient_id": "string",    "available": true  },  "meta": {    "request_id": "req_abc123"  }}