The Latter Years API

Connect your own CRM or website to Latter Years. Post leads and residents, edit them and set their status, read your conversations with families, log a message your system already sent or send one through us, and make a family’s referral link. Every call acts as your organization, through a key you create and can revoke.

The same reference as an OpenAPI 3.1 document, for code generators and API clients: /api/v1/openapi.json

curl -X POST https://latteryears.com/api/v1/intake/leads \
  -H "Authorization: Bearer ly_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: webform-48213" \
  -d '{
    "contact": { "first_name": "Dana", "phone": "(801) 555-0142" },
    "loved_one": { "first_name": "Margaret" },
    "origin_kind": "website"
  }'

Keys

  • An administrator creates a key on the API keys page in Latter Years. A key looks like ly_ followed by 48 characters: a 16-character prefix you will see on the page afterwards, and a 32-character secret shown once, when it is created. We keep only a hash of it.
  • Send it on every request as Authorization: Bearer ly_… over HTTPS.
  • Your organization can have 5 live keys at a time, so you can create the new one, move your system to it, then revoke the old one with no gap.
  • A key can be bound to one of your locations. Then what it posts lands there, and it reads and edits that location’s leads and residents only. A record anywhere else answers 404, as if it did not exist.
  • A key that posts residents carries your attestation: how and when your organization got the families’ consent to be contacted. The contacts it posts are recorded under that attestation, and a key without it cannot post residents.
  • A key acts as the person who created it, so its changes show on your timeline as made via the key’s name. If that person leaves your team, revoke the key and create a new one: until you do, it can still post leads and residents, and everything else answers 403 key_not_usable.

Scopes

Each key holds a set of scopes, chosen when it is created. An endpoint the key has no scope for answers 403 with the scope it needs in required_scope.

residents:write
Send your roster. Needs the consent attestation below. POST /api/v1/intake/residents, POST /api/v1/intake/residents/batch, PATCH /api/v1/residents/{id}
messages:read
Read the messages in your conversations with families. GET /api/v1/conversations, GET /api/v1/conversations/{id}/messages
messages:write
Add a message your own system already sent to the conversation. POST /api/v1/conversations/{id}/messages
messages:send
Send a message to a family, with every rule a typed message follows. POST /api/v1/conversations/{id}/messages
referral_links:write
Create a family link from one of your packets. POST /api/v1/referral-links
metrics:read
Read the numbers on your Reports page. No endpoint uses this scope yet.

Limits

Each limit is per key, counted over the window shown. Over it, the endpoint answers 429 rate_limited; wait and retry. A batch holds at most 500 rows.

POST /api/v1/intake/residents
60 per 60 seconds
POST /api/v1/intake/residents/batch
6 per 60 seconds
POST /api/v1/intake/leads
60 per 60 seconds
POST /api/v1/intake/leads/batch
6 per 60 seconds
GET /api/v1/leads
60 per 60 seconds
PATCH /api/v1/leads/{id}
120 per 60 seconds
PATCH /api/v1/residents/{id}
120 per 60 seconds
GET /api/v1/conversations
60 per 60 seconds
GET /api/v1/conversations/{id}/messages
300 per 60 seconds
POST /api/v1/conversations/{id}/messages (delivery "external")
120 per 60 seconds
POST /api/v1/conversations/{id}/messages (delivery "send")
30 per 60 seconds
POST /api/v1/referral-links
60 per 60 seconds

Retries and Idempotency-Key

  • Send an Idempotency-Key header (1 to 255 characters, one you make up for each new request) on every write. Logging or sending a message and making a referral link require it, and answer 400 idempotency_key_required without it. Posting and editing leads and residents take it too, and you should send it.
  • A retry with the same key and the same body never does the thing twice. It answers the first result again, with replayed: true.
  • The same key with a different body answers 409 idempotency_key_reuse. Use a new key for a new request, and never reuse an old one.
  • We remember a key’s answer for 90 days (30 days for a lead or resident post). A referral link’s key past that answers 409 idempotency_key_spent.
  • Sending a message: a 500 send_unconfirmed means something failed once sending had begun, so we cannot say whether the family received it. Read the conversation before you send it again, under a new key. A 500 send_failed means nothing reached the family; send it again under a new key. Either answer stays with its key.
  • A held message was not sent and is not queued. Send it again after retry_at, under a new key.

Errors

Every refusal is JSON with a stable error code to branch on and a message for a person. Field problems come as a list instead.

{ "error": "not_found", "message": "No lead with that id." }
{ "error": "validation_failed", "errors": [ { "field": "contact.email", "message": "contact.email must be a valid email address." } ] }
400
validation_failed with errors: [{ field, message }], invalid_json, or a missing or unusable Idempotency-Key. (Posting leads and residents answers field problems with 422 instead.)
401
unauthorized. One answer for a missing, malformed, unknown or revoked key.
403
forbidden with required_scope, or key_not_usable when the key’s creator has left your team.
404
not_found. One answer for an id that does not exist and one that is outside your organization, or outside the key’s location.
409
The request conflicts with what is there: idempotency_key_reuse, idempotency_key_spent, in_progress, messaging_not_active, or a record not in a state for that change.
413
batch_too_large.
422
Well formed, but this record cannot take it; field names what to fix where one applies.
429
rate_limited.
500
server_error: ours, and nothing was saved. Sending a message also has send_unconfirmed and send_failed (above).
503
not_configured: that endpoint cannot run just now. Try again later.

Endpoints

Every endpoint, with what it takes and what it answers. Fields marked required must be sent; every other field may be left out. An unknown field is refused, not ignored.

post/api/v1/intake/residents

Post one resident

Adds one resident, or updates the one you already sent (matched on external_system and external_id, then phone, then name and date of birth), and answers with its id. The same duplicate detection and consent handling as a CSV import. Contacts are recorded under the attestation on the key. Nothing is sent to the family.

Scope
residents:write
Limit
60 per 60 seconds (shared with posting one lead)
Idempotency-Key
Recommended

Body

external_system
string. up to 100 characters
external_id
string. up to 200 characters
first_namerequired
string. up to 100 characters
last_namerequired
string. up to 100 characters
phone
string
date_of_birth
string
move_in_date
string
status
string. one of: active, discharged, moved_out, deceased
assigned_user_email
string (email). up to 254 characters
location
string. up to 200 characters
origin_kind
string. one of: marketplace, referral_site, placement_agent, professional_referral, website, phone_walk_in, word_of_mouth, event_outreach, other, unknown
origin_name
string. up to 200 characters
contacts
object[]. at most 10
contacts[].namerequired
string. up to 200 characters
contacts[].phone
string
contacts[].email
string (email). up to 254 characters
contacts[].relationshiprequired
string. one of: POA, spouse, child, sibling, friend, other
contacts[].preferred_contact_method
string. one of: call, text, email
contacts[].is_primary
boolean
consent_captured_on
string
consent_source
string. up to 500 characters

Answer (201)

batch_idrequired
string (uuid)
resident_idrequired
string (uuid) or null
statusrequired
string. created or updated (a match on external_id, phone, or name and date of birth). A replay answers the first request’s status as it stands: check it is one of those two before treating the replay as saved.
replayedrequired
boolean

Responses

200
Updated an existing resident, or a replay of the first answer under the same Idempotency-Key.
201
Created.
400
The body is not valid JSON. invalid_json
401
No key, or not a live key. Every failure here is the same answer. unauthorized
403
The key is real but lacks the scope this endpoint needs (required_scope names it). forbidden
409
The Idempotency-Key was already used for a different payload. idempotency_key_reuse
422
A field is refused (errors names each one), or the resident could not be imported (row_rejected), or was not imported because no contact can be reached (row_skipped). validation_failed, row_rejected, row_skipped
429
Over this key’s rate limit for this endpoint. Retry shortly. rate_limited
500
Our failure. Nothing was saved; retry. server_error

post/api/v1/intake/residents/batch

Post many residents

Accepts up to the batch limit of residents at once and imports them in the background. If any row is invalid the whole batch is refused with every field-named error, and nothing is imported. Repeat the request with the same Idempotency-Key to see how far the batch has got.

Scope
residents:write
Limit
6 per 60 seconds (shared with posting many leads)
Idempotency-Key
Recommended

Body

residentsrequired
object[]. At most 500; more answers 413 batch_too_large.
residents[].external_system
string. up to 100 characters
residents[].external_id
string. up to 200 characters
residents[].first_namerequired
string. up to 100 characters
residents[].last_namerequired
string. up to 100 characters
residents[].phone
string
residents[].date_of_birth
string
residents[].move_in_date
string
residents[].status
string. one of: active, discharged, moved_out, deceased
residents[].assigned_user_email
string (email). up to 254 characters
residents[].location
string. up to 200 characters
residents[].origin_kind
string. one of: marketplace, referral_site, placement_agent, professional_referral, website, phone_walk_in, word_of_mouth, event_outreach, other, unknown
residents[].origin_name
string. up to 200 characters
residents[].contacts
object[]. at most 10
residents[].contacts[].namerequired
string. up to 200 characters
residents[].contacts[].phone
string
residents[].contacts[].email
string (email). up to 254 characters
residents[].contacts[].relationshiprequired
string. one of: POA, spouse, child, sibling, friend, other
residents[].contacts[].preferred_contact_method
string. one of: call, text, email
residents[].contacts[].is_primary
boolean
residents[].consent_captured_on
string
residents[].consent_source
string. up to 500 characters

Answer (202)

batch_idrequired
string (uuid)
statusrequired
string. always "processing"
acceptedrequired
integer. -9007199254740991 to 9007199254740991
replayedrequired
boolean. always "false"

Responses

200
A replay under the same Idempotency-Key: the batch and its counts so far.
202
Accepted; the import runs in the background.
400
The body is not valid JSON. invalid_json
401
No key, or not a live key. Every failure here is the same answer. unauthorized
403
The key is real but lacks the scope this endpoint needs (required_scope names it). forbidden
409
The Idempotency-Key was already used for a different payload. idempotency_key_reuse
413
More rows than one batch can hold. batch_too_large
422
At least one row is refused; errors names each row and field. Nothing was imported. validation_failed
429
Over this key’s rate limit for this endpoint. Retry shortly. rate_limited
500
Our failure. Nothing was saved; retry. server_error

post/api/v1/intake/leads

Post one lead

Adds a family who reached out to you, from your website form or another system. A family you already have is an answer, not an error: 200 with status duplicate and the record you have. Posting a lead sends nothing: the family has not agreed to texts or emails from Latter Years yet.

Scope
leads:write
Limit
60 per 60 seconds (shared with posting one resident)
Idempotency-Key
Recommended

Body

contactrequired
object
contact.first_namerequired
string. up to 100 characters
contact.last_name
string. up to 100 characters
contact.phone
string. up to 40 characters
contact.email
string (email). up to 254 characters
loved_onerequired
object
loved_one.first_namerequired
string. up to 100 characters
loved_one.last_name
string. up to 100 characters
loved_one.date_of_birth
string
location
string. up to 200 characters
origin_kindrequired
string. one of: referral_site, placement_agent, professional_referral, website, phone_walk_in, word_of_mouth, event_outreach, other
origin_name
string. up to 200 characters
inquiry_at
string (date-time)
consent_captured_on
string
consent_source
string. up to 500 characters

Answer (201)

batch_idrequired
string (uuid)
lead_idrequired
string (uuid) or null
statusrequired
string. always "created"
replayedrequired
boolean

Responses

200
A family you already have (status duplicate), or a replay of the first answer.
201
Created.
202
Still being saved by an earlier request under the same Idempotency-Key. Ask again in a moment.
400
The body is not valid JSON. invalid_json
401
No key, or not a live key. Every failure here is the same answer. unauthorized
403
The key is real but lacks the scope this endpoint needs (required_scope names it). forbidden
409
The Idempotency-Key was already used for a different payload. idempotency_key_reuse
422
A field is refused (errors names each one), or the lead could not be imported (row_rejected). validation_failed, row_rejected
429
Over this key’s rate limit for this endpoint. Retry shortly. rate_limited
500
Our failure. Nothing was saved; retry. server_error

post/api/v1/intake/leads/batch

Post many leads

Accepts up to the batch limit of leads at once and imports them in the background, all or nothing on validation. A family repeated within one batch is added once.

Scope
leads:write
Limit
6 per 60 seconds (shared with posting many residents)
Idempotency-Key
Recommended

Body

leadsrequired
object[]. At most 500; more answers 413 batch_too_large.
leads[].contactrequired
object
leads[].contact.first_namerequired
string. up to 100 characters
leads[].contact.last_name
string. up to 100 characters
leads[].contact.phone
string. up to 40 characters
leads[].contact.email
string (email). up to 254 characters
leads[].loved_onerequired
object
leads[].loved_one.first_namerequired
string. up to 100 characters
leads[].loved_one.last_name
string. up to 100 characters
leads[].loved_one.date_of_birth
string
leads[].location
string. up to 200 characters
leads[].origin_kindrequired
string. one of: referral_site, placement_agent, professional_referral, website, phone_walk_in, word_of_mouth, event_outreach, other
leads[].origin_name
string. up to 200 characters
leads[].inquiry_at
string (date-time)
leads[].consent_captured_on
string
leads[].consent_source
string. up to 500 characters

Answer (202)

batch_idrequired
string (uuid)
statusrequired
string. always "processing"
acceptedrequired
integer. -9007199254740991 to 9007199254740991
replayedrequired
boolean. always "false"

Responses

200
A replay under the same Idempotency-Key: the batch and its counts so far.
202
Accepted; the import runs in the background.
400
The body is not valid JSON. invalid_json
401
No key, or not a live key. Every failure here is the same answer. unauthorized
403
The key is real but lacks the scope this endpoint needs (required_scope names it). forbidden
409
The Idempotency-Key was already used for a different payload. idempotency_key_reuse
413
More rows than one batch can hold. batch_too_large
422
At least one row is refused; errors names each row and field. Nothing was imported. validation_failed
429
Over this key’s rate limit for this endpoint. Retry shortly. rate_limited
500
Our failure. Nothing was saved; retry. server_error

get/api/v1/leads

List leads

Your leads (at the key’s location, when it has one), oldest change first, for a sync. Pass next_cursor back as cursor for the next page. A change appears about ten seconds after it is made, and a tag change alone does not move updated_at. contact is null while the family has not shared it with you.

Scope
leads:write
Limit
60 per 60 seconds

Query

since
string (date-time)
cursor
string. up to 200 characters
limit
integer. 1 to 100

Answer (200)

leadsrequired
object[]
leads[].idrequired
string (uuid)
leads[].location_idrequired
string (uuid) or null
leads[].listing_idrequired
string (uuid) or null
leads[].statusrequired
string
leads[].outcomerequired
string or null
leads[].close_reasonrequired
string or null
leads[].patient_statusrequired
string or null
leads[].automations_pausedrequired
boolean or null
leads[].origin_kindrequired
string or null
leads[].origin_namerequired
string or null
leads[].intake_channelrequired
string or null
leads[].inquiry_atrequired
string or null. An ISO 8601 time.
leads[].created_atrequired
string. An ISO 8601 time.
leads[].updated_atrequired
string. An ISO 8601 time.
leads[].created_by_api_key_idrequired
string (uuid) or null
leads[].tagsrequired
string[]
leads[].loved_onerequired
object
leads[].loved_one.first_namerequired
string or null
leads[].loved_one.last_namerequired
string or null
leads[].contactrequired
object or null
leads[].contact.first_namerequired
string or null
leads[].contact.last_namerequired
string or null
leads[].contact.phonerequired
string or null
leads[].contact.emailrequired
string or null
next_cursorrequired
string or null

Responses

200
One page of leads.
400
A query value is refused, or the cursor is not one this API returned. validation_failed
401
No key, or not a live key. Every failure here is the same answer. unauthorized
403
The key is real but may not do this: it lacks the scope (forbidden, naming required_scope), or the person who created it is no longer on your team (key_not_usable). forbidden, key_not_usable
429
Over this key’s rate limit for this endpoint. Retry shortly. rate_limited
500
Our failure. Nothing was saved; retry. server_error

patch/api/v1/leads/{id}

Edit a lead or set its status

Changes only what you send. contact can change while the family has not taken over their own details; tags add and remove (a new name joins your tag list); status is contacted, open, or closed with a close_reason; an agency can set patient_status. Nothing is ever deleted: a lead is closed. Anything the family wrote cannot be sent and is refused. A status that stops messages pauses the family’s sequences, as it does on the lead page. Every change shows on the lead’s timeline as made via your key.

Scope
leads:write
Limit
120 per 60 seconds
Idempotency-Key
Recommended

Body

contact
object
contact.first_name
string. up to 100 characters
contact.last_name
string or null. up to 100 characters
contact.phone
string or null
contact.email
string (email) or null. up to 254 characters
inquiry_at
string (date-time)
tags
object
tags.add
string[]. at most 20
tags.remove
string[]. at most 20
status
string. one of: contacted, open, closed
close_reason
string. one of: no_availability, not_a_fit, lost_to_other, unresponsive, passed_away, hospitalized, stayed_home, another_agency, could_not_staff, outside_area, payment, care_not_provided, no_response, declined, referral_elsewhere, moved_nursing_home, hospice_instead, home_health_instead
close_reason_text
string. up to 200 characters
close_note
string. up to 5000 characters
close_payer
string
close_same_organization
boolean
close_checkin_days
integer. 1 to 30
patient_status
string or null. one of: passed_away, back_in_hospital, moved_to_hospice, left_hospice_alive, moved_assisted_living, moved_nursing_home, care_finished, switched_agency, asked_to_stop, back_on_service
patient_status_same_organization
boolean

Answer (200)

lead_idrequired
string (uuid)
changedrequired
string[]. The fields this change wrote.
replayedrequired
boolean

Responses

200
Saved (or a replay of the first answer under the same Idempotency-Key).
400
The body or query is not one this endpoint takes: invalid JSON, or a field it refuses, named in errors. validation_failed, invalid_json
401
No key, or not a live key. Every failure here is the same answer. unauthorized
403
The key is real but may not do this: it lacks the scope (forbidden, naming required_scope), or the person who created it is no longer on your team (key_not_usable). forbidden, key_not_usable
404
No lead with that id for this key. not_found
409
The lead is not in a state for that change, or the Idempotency-Key was used for a different change. family_owns_contact, contact_opted_out, status_conflict, family_closed, care_not_started, retry, idempotency_key_reuse
422
A well-formed change this lead cannot take; field names it. not_applicable, invalid_value, invalid_tag, invalid_close_reason, too_many_tags, tag_list_full, same_organization_required
429
Over this key’s rate limit for this endpoint. Retry shortly. rate_limited
500
Our failure. Nothing was saved; retry. server_error

patch/api/v1/residents/{id}

Edit a resident or set their status

Changes only what you send: names, phone, dates, status with its move_out_date and move_out_reason (they travel together), tags, and for an agency patient_status. Nothing is ever deleted: a resident moves out.

Scope
residents:write
Limit
120 per 60 seconds
Idempotency-Key
Recommended

Body

first_name
string. up to 100 characters
last_name
string. up to 100 characters
phone
string or null
date_of_birth
string or null
move_in_date
string or null
status
string. one of: active, discharged, moved_out, deceased
move_out_date
string or null
move_out_reason
string or null. one of: higher_level_of_care, hospital, deceased, cost, family_decision, dissatisfied, other
tags
object
tags.add
string[]. at most 20
tags.remove
string[]. at most 20
patient_status
string or null. one of: passed_away, back_in_hospital, moved_to_hospice, left_hospice_alive, moved_assisted_living, moved_nursing_home, care_finished, switched_agency, asked_to_stop, back_on_service
patient_status_kind
string. one of: home_health, hospice, personal_care
patient_status_same_organization
boolean

Answer (200)

resident_idrequired
string (uuid)
changedrequired
string[]. The fields this change wrote.
replayedrequired
boolean

Responses

200
Saved (or a replay of the first answer under the same Idempotency-Key).
400
The body or query is not one this endpoint takes: invalid JSON, or a field it refuses, named in errors. validation_failed, invalid_json
401
No key, or not a live key. Every failure here is the same answer. unauthorized
403
The key is real but may not do this: it lacks the scope (forbidden, naming required_scope), or the person who created it is no longer on your team (key_not_usable). forbidden, key_not_usable
404
No resident with that id for this key. not_found
409
The resident is not in a state for that change, or the Idempotency-Key was used for a different change. care_not_started, retry, idempotency_key_reuse
422
A well-formed change this resident cannot take; field names it. not_applicable, invalid_value, invalid_tag, too_many_tags, tag_list_full, same_organization_required
429
Over this key’s rate limit for this endpoint. Retry shortly. rate_limited
500
Our failure. Nothing was saved; retry. server_error

get/api/v1/conversations

List conversations

The leads whose thread changed, oldest change first. id is the lead’s id; last_activity_at moves when a message arrives or the lead’s status changes. Takes since, cursor and limit as leads do (a cursor works only where it came from).

Scope
messages:read
Limit
60 per 60 seconds

Query

since
string (date-time)
cursor
string. up to 200 characters
limit
integer. 1 to 100

Answer (200)

conversationsrequired
object[]
conversations[].idrequired
string (uuid)
conversations[].location_idrequired
string (uuid) or null
conversations[].listing_idrequired
string (uuid) or null
conversations[].last_activity_atrequired
string
conversations[].last_message_atrequired
string or null
next_cursorrequired
string or null

Responses

200
One page of conversations.
400
A query value is refused, or the cursor is not one this API returned. validation_failed
401
No key, or not a live key. Every failure here is the same answer. unauthorized
403
The key is real but may not do this: it lacks the scope (forbidden, naming required_scope), or the person who created it is no longer on your team (key_not_usable). forbidden, key_not_usable
429
Over this key’s rate limit for this endpoint. Retry shortly. rate_limited
500
Our failure. Nothing was saved; retry. server_error

get/api/v1/conversations/{id}/messages

Read one conversation

One lead’s thread as the family saw it, oldest change first. A message sent securely is listed with held_in_app true and no body; the text or email that told the family about it is its own message with secure_notice true. Notes, the assistant’s reasoning, drafts never sent and other organizations’ messages are never included. Each thread you read is recorded in your access log under the key’s creator.

Scope
messages:read
Limit
300 per 60 seconds

Query

since
string (date-time)
cursor
string. up to 200 characters
limit
integer. 1 to 100

Answer (200)

messagesrequired
object[]
messages[].idrequired
string (uuid)
messages[].conversation_idrequired
string (uuid)
messages[].directionrequired
string. one of: inbound, outbound
messages[].channelrequired
string. one of: sms, email, portal
messages[].bodyrequired
string or null
messages[].held_in_apprequired
boolean
messages[].secure_noticerequired
boolean
messages[].statusrequired
string
messages[].subjectrequired
string or null
messages[].sent_atrequired
string or null
messages[].received_atrequired
string or null
messages[].updated_atrequired
string
messages[].authorrequired
object
messages[].author.kindrequired
string. one of: family, staff, assistant, automation, api_key
messages[].author.api_key_idrequired
string (uuid) or null
messages[].author.api_key_labelrequired
string or null
next_cursorrequired
string or null

Responses

200
One page of messages.
400
A query value is refused, or the cursor is not one this API returned. validation_failed
401
No key, or not a live key. Every failure here is the same answer. unauthorized
403
The key is real but may not do this: it lacks the scope (forbidden, naming required_scope), or the person who created it is no longer on your team (key_not_usable). forbidden, key_not_usable
404
No conversation with that id for this key. not_found
429
Over this key’s rate limit for this endpoint. Retry shortly. rate_limited
500
Our failure. Nothing was saved; retry. server_error

post/api/v1/conversations/{id}/messages

Log a message you sent, or send one

delivery "external" adds a text or email your own system already sent: we store it and never send it, and it counts as your team answering. delivery "send" sends it exactly as a reply typed in Latter Years is sent: every opt-out, consent rule, quiet hours and your daily allowance apply. Left out, channel is a text when the family has shared a phone, otherwise their Latter Years page, and a refusal is never retried on another channel. sensitive true (or anything we find about health or money) keeps the words on their Latter Years page and the family gets a notice with a secure link. outcome says what happened: sent, sent_as_secure_notice, held (send it again after retry_at under a new Idempotency-Key; nothing is queued), or refused.

Scope
messages:write for delivery "external"; messages:send for delivery "send"
Limit
120 per 60 seconds for delivery "external"; 30 per 60 seconds for delivery "send"
Idempotency-Key
Required

Body

delivery: "external"

deliveryrequired
string. always "external"
channelrequired
string. one of: sms, email
bodyrequired
string. up to 5000 characters
subject
string. up to 200 characters
sensitive
boolean

delivery: "send"

deliveryrequired
string. always "send"
channel
string. one of: sms, email, portal
bodyrequired
string. up to 5000 characters
subject
string. up to 200 characters
sensitive
boolean

Answer (201)

delivery: "external"

message_idrequired
string (uuid)
conversation_idrequired
string (uuid)
deliveryrequired
string. always "external"
channelrequired
string. one of: sms, email
held_in_apprequired
boolean
logged_atrequired
string
replayedrequired
boolean

delivery: "send"

conversation_idrequired
string (uuid)
deliveryrequired
string. always "send"
outcomerequired
string. one of: sent, sent_as_secure_notice, held, refused
channelrequired
string. one of: sms, email, portal
message_idrequired
string (uuid) or null
notice_channelrequired
string or null. one of: in_app, sms, email
deliveredrequired
boolean or null
mockedrequired
boolean or null
reasonrequired
string or null
reason_messagerequired
string or null
retry_atrequired
string or null
answered_atrequired
string
replayedrequired
boolean

Responses

200
A replay under the same Idempotency-Key and body: the first answer again, and never a second send.
201
Logged (delivery external) or answered (delivery send: read outcome).
400
No usable Idempotency-Key header, invalid JSON, a field refused (errors names it), or an email with no subject. validation_failed, invalid_json, idempotency_key_required, idempotency_key_invalid
401
No key, or not a live key. Every failure here is the same answer. unauthorized
403
The key is real but may not do this: it lacks the scope (forbidden, naming required_scope), or the person who created it is no longer on your team (key_not_usable). forbidden, key_not_usable
404
No conversation with that id for this key. not_found
409
The Idempotency-Key was used for a different message; the first send under it is still going (in_progress: ask again in a moment); or messaging is not active for this lead yet, where your team could not reply either. idempotency_key_reuse, in_progress, messaging_not_active
429
Over this key’s rate limit for this endpoint. Retry shortly. rate_limited
500
send_unconfirmed: something failed once sending had begun, so we cannot confirm whether the family received it. Read the conversation before sending again, under a new key. send_failed: nothing reached the family; send it again under a new key. Both answers stay with that Idempotency-Key. server_error: nothing was sent; the same key may be retried. send_unconfirmed, send_failed, server_error

get/api/v1/openapi.json

This document

The OpenAPI 3.1 description of every /api/v1 endpoint. No key needed.

Scope
None: no key needed.

Responses

200
The OpenAPI document.