Skip to content
Docs menu

Contacts

People your practice knows — leads, prospects, patients, churned. Contacts hold encrypted PHI and respect consent flags on outbound channels.

The contact object

All identifying fields (email, phone, address, dob, notes) are stored encrypted at rest. Reads decrypt server-side based on the caller's role. lifecycle is one of LEAD, PROSPECT, PATIENT, INACTIVE, CHURNED.

POST/api/contactsBearer token

Create a contact

Create a new contact in the workspace. The authenticated user/service-account becomes the owner unless an explicit owner is set elsewhere.

Required scopes:contacts.write

Request body

NameTypeDescription
firstNamerequiredstringGiven name. 1–100 characters.
lastNamerequiredstringFamily name. 1–100 characters.
emailstringEmail address. Must be a valid format if provided.
phonestringE.164 phone number recommended. 7–40 chars.
dobstringDate of birth. ISO 8601 date string.
addressstringMailing address — free-form.
notesstringFree-text notes. ≤ 8000 chars.
lifecyclestringOne of LEAD, PROSPECT, PATIENT, INACTIVE, CHURNED.
sourcestringWhere the contact came from. ≤ 120 chars.
tagsstring[]Up to 20 short tags.
consentEmailbooleanPatient consents to marketing email.
consentSmsbooleanPatient consents to SMS.
consentHipaabooleanHIPAA disclosure acknowledged.

Request body

JSON
{
  "firstName": "Avery",
  "lastName": "Liu",
  "email": "avery@example.com",
  "phone": "+15555550123",
  "lifecycle": "PROSPECT",
  "consentEmail": true
}

Responses

  • 201 CreatedCreated.
    example
    {
      "id": "cln1f8s7p0001a8l1c2k3d4e5",
      "firstName": "Avery",
      "lastName": "Liu",
      "email": "avery@example.com",
      "phone": "+15555550123",
      "lifecycle": "PROSPECT",
      "source": null,
      "tags": [],
      "ownerId": "usr_8h2c3p…",
      "consentEmail": true,
      "consentSms": false,
      "consentHipaa": false,
      "createdAt": "2026-05-18T14:00:00Z",
      "updatedAt": "2026-05-18T14:00:00Z"
    }
  • 401 UnauthorizedBearer token missing or invalid.
  • 403 ForbiddenToken lacks contacts.write.
  • 422 Unprocessable EntityValidation failed.

Code samples

Shell
curl -X POST https://app.contactfollowup.com/api/contacts \
  -H "Authorization: Bearer $HEARTHNOTE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "firstName": "Avery",
  "lastName": "Liu",
  "email": "avery@example.com",
  "phone": "+15555550123",
  "lifecycle": "PROSPECT",
  "consentEmail": true
}'
GET/api/contactsBearer token

List contacts

Return the contacts the caller can see, filtered by query / lifecycle. Results are scoped to the caller's role and any ownership rules.

Required scopes:contacts.read

Query parameters

NameTypeDescription
qstringFull-text search over name / email / phone.
lifecyclestringFilter by lifecycle stage.
limitintegerPage size. Default 50, max 200.

Responses

  • 200 OKContacts the caller can see.
    example
    {
      "contacts": [
        {
          "id": "cln1f8s7p0001a8l1c2k3d4e5",
          "firstName": "Avery",
          "lastName": "Liu",
          "email": "avery@example.com",
          "phone": "+15555550123",
          "lifecycle": "PROSPECT",
          "source": null,
          "tags": [],
          "ownerId": "usr_8h2c3p…",
          "consentEmail": true,
          "consentSms": false,
          "consentHipaa": false,
          "createdAt": "2026-05-18T14:00:00Z",
          "updatedAt": "2026-05-18T14:00:00Z"
        }
      ]
    }
  • 403 ForbiddenMissing contacts.read.

Code samples

Shell
curl -X GET https://app.contactfollowup.com/api/contacts \
  -H "Authorization: Bearer $HEARTHNOTE_TOKEN" \
  -H "Content-Type: application/json"
GET/api/contacts/{id}Bearer token

Retrieve a contact

Fetch a single contact by id.

Required scopes:contacts.read

Path parameters

NameTypeDescription
idrequiredstringContact id.

Responses

  • 200 OKThe contact.
    example
    {
      "id": "cln1f8s7p0001a8l1c2k3d4e5",
      "firstName": "Avery",
      "lastName": "Liu",
      "email": "avery@example.com",
      "phone": "+15555550123",
      "lifecycle": "PROSPECT",
      "source": null,
      "tags": [],
      "ownerId": "usr_8h2c3p…",
      "consentEmail": true,
      "consentSms": false,
      "consentHipaa": false,
      "createdAt": "2026-05-18T14:00:00Z",
      "updatedAt": "2026-05-18T14:00:00Z"
    }
  • 404 Not FoundNot visible to the caller, or doesn't exist.

Code samples

Shell
curl -X GET https://app.contactfollowup.com/api/contacts/{id} \
  -H "Authorization: Bearer $HEARTHNOTE_TOKEN" \
  -H "Content-Type: application/json"
PATCH/api/contacts/{id}Bearer token

Update a contact

Partial update. Only the fields you send change. PHI fields re-encrypt server-side.

Required scopes:contacts.write

Path parameters

NameTypeDescription
idrequiredstringContact id.

Request body

NameTypeDescription
firstNamestringNew given name.
lastNamestringNew family name.
emailstringNew email (use null to clear).
phonestringNew phone.
lifecyclestringNew lifecycle stage.
tagsstring[]Replace the tag list.

Request body

JSON
{ "lifecycle": "PATIENT", "tags": ["new-intake"] }

Responses

  • 200 OKUpdated record.
    example
    {
      "id": "cln1f8s7p0001a8l1c2k3d4e5",
      "firstName": "Avery",
      "lastName": "Liu",
      "email": "avery@example.com",
      "phone": "+15555550123",
      "lifecycle": "PROSPECT",
      "source": null,
      "tags": [],
      "ownerId": "usr_8h2c3p…",
      "consentEmail": true,
      "consentSms": false,
      "consentHipaa": false,
      "createdAt": "2026-05-18T14:00:00Z",
      "updatedAt": "2026-05-18T14:00:00Z"
    }
  • 422 Unprocessable EntityValidation failed.

Code samples

Shell
curl -X PATCH https://app.contactfollowup.com/api/contacts/{id} \
  -H "Authorization: Bearer $HEARTHNOTE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "lifecycle": "PATIENT", "tags": ["new-intake"] }'
DELETE/api/contacts/{id}Bearer token (ADMIN)

Soft-delete a contact

Tombstones the contact: nulls all encrypted PHI columns, sets deletedAt, writes an immutable audit row. ADMIN only.

Required scopes:contacts.delete

Path parameters

NameTypeDescription
idrequiredstringContact id.

Responses

  • 204 No ContentTombstoned. No body returned.
  • 403 ForbiddenCaller is not an ADMIN.
  • 404 Not FoundContact doesn't exist.

Code samples

Shell
curl -X DELETE https://app.contactfollowup.com/api/contacts/{id} \
  -H "Authorization: Bearer $HEARTHNOTE_TOKEN" \
  -H "Content-Type: application/json"