Send messages to patients
Reach a patient with one request. Send a message to a patient takes a patient ID or phone number and the text. BloomText finds the patient (or creates one for a new number), finds or starts the conversation on the right clinic phone number, and sends the message.
cURL
curl -X POST https://api.bloomtext.com/v1/patient-messages \
-H "Authorization: Bearer $BLOOMTEXT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"to": { "patient_id": "5a1c9e2b-7d44-4f1a-9c3e-2b8d6f0a1e77" },
"body": "Hi Jane, Dr. Example moved your appointment to Friday at 9:00. Reply here with any questions."
}'The response holds the sent message, with its conversation, patient, channel, and delivery status, plus patient_created and conversation_created.
Use an API key or an org admin’s OAuth token. The request needs only the messages.write scope, even when it creates a patient.
Who it comes from
| Sent with | The patient sees | Staff see |
|---|---|---|
| An API key | The clinic | The key’s name, like “Reminder service” |
| OAuth | The admin | The admin, with a “via” label naming your app |
With OAuth, sending also marks the conversation as read for the admin, as replying in BloomText does. See Messages sent through the API.
Who gets it
Set to to exactly one of:
to | What happens |
|---|---|
{"patient_id": "…"} | Sends to that patient. |
{"phone": "…"} | Looks up the phone number in your organization’s patients. |
A phone number can be in any common format, like (512) 555-0143 or +15125550143. BloomText resolves it this way:
- No patient has that number: BloomText creates one, as it does when a new number texts the clinic. Add a
patientobject withfirst_name,last_name,date_of_birth,email, ormrnto fill in the new record. Ifpatient.mrnmatches an existing patient, BloomText uses that patient instead of creating one. - One patient has it: that patient.
- Several patients share it, like a family phone:
409 ambiguous_recipient. BloomText never guesses; the error’scandidateslists the matching patients. Send again with the rightpatient_id, or addpatient.mrnto say which one.
{
"to": {
"phone": "(512) 555-0199",
"patient": { "first_name": "John", "last_name": "Doe", "mrn": "MRN-21004" }
},
"body": "Welcome to Example Clinic. Your intake form is ready."
}Secure or plain text
Messages to patients are secure unless you set channel to sms:
channel | The patient receives | Use it for |
|---|---|---|
secure (default) | A text or email with a secure link. The message itself stays inside BloomText, and the patient reads and replies there. | Anything clinical or personal. |
sms | The message text as a regular SMS from the clinic’s number. Replies come back by SMS. | Logistics, like reminders, directions, or “your forms are ready.” |
{
"to": { "patient_id": "5a1c9e2b-7d44-4f1a-9c3e-2b8d6f0a1e77" },
"body": "Reminder: your appointment is tomorrow at 9:00 at 1200 Main St.",
"channel": "sms"
}Plain SMS puts the message on the patient’s phone and the carrier’s network, outside BloomText. Keep clinical detail out of sms messages.
Which number it comes from
Each clinic phone number belongs to a team, like “Front desk” at +1 512-555-0100 or “Billing” at +1 512-555-0200. Texts come from one of them, and the patient’s replies go back to that team’s conversation. Name the number with from:
{
"to": { "patient_id": "5a1c9e2b-7d44-4f1a-9c3e-2b8d6f0a1e77" },
"from": "+15125550200",
"body": "Your statement is ready. Reply here with any billing questions."
}- List phone numbers returns the numbers you can send from: every number in the organization for an API key, or the admin’s teams’ numbers for OAuth.
- Without
from, BloomText uses the default number, markedis_default: the one set on the API key when it was created, or the admin’s own default. - If there’s no default and more than one number, the request returns
422 from_required. Passfrom.
BloomText keeps one conversation per phone number and patient. Sending again from the same number continues that conversation; sending from another team’s number starts a separate one.
Reply in a conversation
With an API key, reply to a patient with this same request: name the patient and the from number, and BloomText continues their conversation on that number.
With OAuth, you can also reply by conversation ID, for example one you found by reading messages. Send a message in the conversation (POST /me/conversations/{id}/messages); it uses the conversation’s phone number and channel.
curl -X POST https://api.bloomtext.com/v1/me/conversations/e5c3b7b8-8f08-4d5a-9af1-0d11b0f4b7a0/messages \
-H "Authorization: Bearer $BLOOMTEXT_ACCESS_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"body": "Friday at 9:00 works. You are all set."}'Delivery status
A text can fail after BloomText accepts it: a landline, a disconnected number, or a carrier block. Every message to a patient carries a delivery status that BloomText updates from the carrier.
delivery.status | Meaning |
|---|---|
queued | Accepted, not yet handed to the carrier. |
sent | Handed to the carrier. |
delivered | The carrier confirmed delivery. |
opened | The patient opened the secure link. |
failed, undelivered | The message didn’t reach the patient. delivery.error says why, like landline, invalid_number, or opted_out. |
To follow up on failures, read messages with after set to the start of the day and q=is:failed, or check one message with Retrieve a patient message.
Opt-outs
A patient who replies STOP to texts from a phone number stops getting SMS from that number. Their patient record shows sms_opted_out: true, and sending them channel: sms returns 422 patient_opted_out. Secure messages by email still reach them. When they reply START, texts resume.
Errors to handle
| Status | Code | What to do |
|---|---|---|
| 404 | not_found | The patient_id doesn’t exist in your organization, or from isn’t one of its numbers. |
| 409 | ambiguous_recipient | Pick a patient from candidates and send with patient_id, or add patient.mrn. |
| 422 | from_required | Pass a from number from List phone numbers. |
| 422 | no_contact_method | The patient has no phone number for sms, or neither phone nor email for secure. Update the patient. |
| 422 | patient_opted_out | Send with channel: secure so it goes by email, or wait for the patient to reply START. |
Every send takes an Idempotency-Key, so a retry after a timeout never texts a patient twice. For automated jobs, derive the key from something stable, like the appointment ID. See Idempotency.
Sending to many patients
Every send names one patient; there’s no “send to everyone” call. To send the same message to many patients, use a campaign: add the recipients, start it, and BloomText sends to each one in turn and tracks delivery. For a different message per patient, loop over this endpoint within your rate limits, and queue the sends rather than firing hundreds at once.