Skip to content
Docs menu

Secure messaging

Encrypted patient threads. Bodies AES-256-GCM at rest, attachments behind signed URLs.

Threads are owned by a contact and assigned to a staff user. Messages within a thread are managed through follow-up endpoints; today only thread CRUD is exposed publicly. archived=true on list filters in archived threads.

Open a thread

Create a new secure-message thread with a patient.

Required scopes:contacts.write

Request body

NameTypeDescription
contactIdrequiredstringPatient.
subjectstringThread subject. ≤ 500 chars.
assigneeUserIdstringStaff user to route to.

Request body

JSON
{ "contactId": "cln…", "subject": "Pre-visit questions" }

Responses

  • 201 CreatedCreated.
    example
    {
      "id": "smt_4k9d2x…",
      "contactId": "cln1f8s7p…",
      "subject": "Pre-visit questions",
      "assigneeUserId": "usr_8h2c3p…",
      "isArchived": false,
      "lastMessageAt": "2026-05-18T14:00:00Z",
      "createdAt": "2026-05-18T13:55:00Z"
    }

Code samples

Shell
curl -X POST https://app.contactfollowup.com/api/secure-messages/threads \
  -H "Authorization: Bearer $HEARTHNOTE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "contactId": "cln…", "subject": "Pre-visit questions" }'

List threads

Filter by assignee, contact, archived flag.

Required scopes:contacts.read

Query parameters

NameTypeDescription
assigneeUserIdstringStaff user filter.
contactIdstringPatient filter.
archivedbooleanInclude archived threads.
limitintegerDefault 100.

Responses

  • 200 OKThreads.
    example
    { "threads": [{
      "id": "smt_4k9d2x…",
      "contactId": "cln1f8s7p…",
      "subject": "Pre-visit questions",
      "assigneeUserId": "usr_8h2c3p…",
      "isArchived": false,
      "lastMessageAt": "2026-05-18T14:00:00Z",
      "createdAt": "2026-05-18T13:55:00Z"
    }] }

Code samples

Shell
curl -X GET https://app.contactfollowup.com/api/secure-messages/threads \
  -H "Authorization: Bearer $HEARTHNOTE_TOKEN" \
  -H "Content-Type: application/json"