# Send appointment reminders

> Send next-day appointment reminders into BloomText conversations from your EMR or scheduling system.

Source: https://www.bloomtext.com/developers/api/guides/appointment-reminders/

This guide builds a nightly job that reads tomorrow's appointments from your scheduling system and posts a reminder into each patient's BloomText conversation. Replies land in the same thread your front desk already works from.

**You'll need:** an app user with `conversations:read` and `messages:write`, and a way to read appointments from your EMR or scheduler.

### Map patients to conversations

The BloomText API doesn't look up patients by name or phone. Your app keeps a mapping from your patient ID to the BloomText conversation ID, created when the patient's conversation is set up.

```sql filename="schema.sql"
create table bloomtext_conversations (
  patient_id       text primary key,   -- your EMR's patient ID
  conversation_id  uuid not null       -- BloomText conversation ID
);
```

### Build the reminder text

Keep reminders short and free of clinical detail. Times, locations, and a reply instruction are enough.

```text filename="Template"
Reminder: you have an appointment tomorrow at {time} with {clinic}. Reply C to confirm or R to reschedule.
```

### Send one message per appointment

Derive the idempotency key from the appointment ID and date, so rerunning the job never double-sends.

```js filename="send-reminders.mjs"
const API = 'https://api.bloomtext.com/v1'

export async function sendReminders(appointments, lookupConversation) {
  for (const appointment of appointments) {
    const conversationId = await lookupConversation(appointment.patientId)
    if (!conversationId) continue // no BloomText conversation for this patient yet

    const response = await fetch(`${API}/conversations/${conversationId}/messages`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.BLOOMTEXT_API_KEY}`,
        'Idempotency-Key': `reminder:${appointment.id}:${appointment.date}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        body: `Reminder: you have an appointment tomorrow at ${appointment.time}. Reply C to confirm or R to reschedule.`,
      }),
    })

    if (response.status === 429) {
      await new Promise((r) => setTimeout(r, Number(response.headers.get('Retry-After')) * 1000))
      continue // pick it up on the next run; the idempotency key keeps it safe
    }
    const message = await response.json()
    console.log(appointment.id, message.id)

    await new Promise((r) => setTimeout(r, 1100)) // stay under 60 requests per minute
  }
}
```

```python filename="send_reminders.py"
import os
import time

import requests

API = "https://api.bloomtext.com/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['BLOOMTEXT_API_KEY']}"


def send_reminders(appointments, lookup_conversation):
    for appointment in appointments:
        conversation_id = lookup_conversation(appointment["patient_id"])
        if not conversation_id:
            continue  # no BloomText conversation for this patient yet

        response = session.post(
            f"{API}/conversations/{conversation_id}/messages",
            headers={"Idempotency-Key": f"reminder:{appointment['id']}:{appointment['date']}"},
            json={
                "body": f"Reminder: you have an appointment tomorrow at {appointment['time']}. "
                "Reply C to confirm or R to reschedule."
            },
        )
        if response.status_code == 429:
            time.sleep(int(response.headers["Retry-After"]))
            continue  # pick it up on the next run; the idempotency key keeps it safe
        response.raise_for_status()
        print(appointment["id"], response.json()["id"])

        time.sleep(1.1)  # stay under 60 requests per minute
```

### Schedule it

Run the job once a day, in the evening, from cron or your job scheduler.

```bash filename="crontab"
0 18 * * * node /srv/reminders/send-reminders.mjs >> /var/log/reminders.log 2>&1
```

## Next

Turn "C" replies into confirmations in your EMR with [Handle patient replies](https://www.bloomtext.com/developers/api/guides/handle-replies/).
