Spruce Integration Guide
Interacting with the Spruce Public API for integration with external systems
Integration Guide
Here's how Spruce's Public API supports keeping Spruce and an external system (EMR, CRM, PMS, scheduling, billing, etc.) in sync. At a high level, there are three pieces:
- Push contact demographics into Spruce (if missing) using the REST Contact endpoints so every record in your system has a corresponding Spruce contact your team can message.
- Link each Spruce contact back to the external record with Integration Links so staff can round-trip between systems and your sync job can match records deterministically.
- Subscribe to Spruce Webhooks to receive near-real-time notifications when demographics change in Spruce or when conversation activity happens, then write those back to your system.
Full API reference: https://developer.sprucehealth.com/reference/overview
All endpoints below live under the production REST API at https://api.sprucehealth.com/v1.
Authentication
All Public API calls use HTTP Basic or Bearer auth with a base64-encoded credential pair:
Authorization: Bearer <base64(access_id:secret_key)>
Access IDs and secret keys are issued per organization. Customers can request API access at https://sprucehealth.com/spruce-api. A method of secure exchange for these secrets with integration partners can be discussed with us as part of the integration development process.
Syncing Contact Demographics into Spruce
Initial backfill and ongoing create
Create a Spruce contact for each record that doesn't already exist in Spruce with POST /v1/contacts. You can pass an s-idempotency-key header (≤255 chars) to make retries safe — we recommend a stable value derived from the record ID in your system.
Minimum requirement: at least one of givenName, familyName, phoneNumbers, faxNumbers, or emailAddresses.
Example request body:
{
"category": "patient",
"givenName": "Joe",
"middleName": "William",
"familyName": "Smith",
"dateOfBirth": "2000-01-30",
"gender": "male",
"phoneNumbers": [{ "value": "+12225550000", "label": "Mobile" }],
"emailAddresses": [{ "value": "[email protected]", "label": "Home" }]
}The response returns the full contact, including the Spruce contact id (entity_…) you should persist against the external record.
Updating demographics
PATCH /v1/contacts/{contactId} accepts the same fields. Omitted fields are unchanged; empty strings or empty arrays clear a field. Use this when a record's phone number, address, name, etc. change in your system.
Deletion
DELETE /v1/contacts/{contactId}. Honor the canDelete flag on the contact before calling — contacts involved in active conversations typically aren't deletable.
Search / Lookup
POST /v1/contacts/search supports free-text and structured filters (name, phone, email, tags, category, gender, account status, and — see below — integration link). Useful for reconciliation jobs or when webhook events reference a contact you haven't seen before.
Tags and custom fields
- Tags: list and create via https://developer.sprucehealth.com/reference/contacttags and https://developer.sprucehealth.com/reference/createcontacttag. Create tags once per org, then attach to contacts via
tagIdson create/update. - Organization contact fields: list and create via https://developer.sprucehealth.com/reference/contactfields and https://developer.sprucehealth.com/reference/createcontactfield. Define custom fields once per org (e.g. "MRN", "Preferred Pharmacy"), then set per-contact values via
organizationContactFields.
This is the best place to stamp metadata from the external system (MRN, insurance, primary care provider, account number, etc.) so your team sees it alongside the conversation.
Linking Spruce Contacts to the External System
Use Integration Links to associate a Spruce contact with its record in the external system. This gives staff a deep link from the Spruce UI back to the external system and gives your webhook consumer a canonical external ID to resolve back to the source record.
-
Create:
POST /v1/contacts/{contactId}/integrationlinkswith thecustomtype. Custom-type links include a URL that opens the record in your system:{ "type": "custom", "externalId": "ext_patient_12345", "url": "https://my-system.example.com/patient/12345" } -
Delete:
DELETE /v1/contacts/{contactId}/integrationlinks— remove bytype+externalId.
Looking up a Spruce contact by external ID
Once integration links are in place, you can find the Spruce contact for a given external record directly by passing an integrationIDFilter to POST /v1/contacts/search:
{
"structured": {
"integrationIDFilter": [
{
"integrationIDs": [
{ "integrationLinkType": "custom", "id": "ext_patient_12345" }
],
"match": "any"
}
]
}
}The contact payload (including webhook payloads) echoes integrationLinks back to you, so when you receive a contact.updated or conversation.created event you can resolve the Spruce contact directly to the external record without an extra call.
Receiving Change Notifications via Webhooks
Spruce Webhooks push events to a URL you own when contacts, conversations, or messages change. This is the mechanism for detecting demographic changes and conversation activity.
- Webhooks overview, event envelope, signature verification, delivery guarantees, and endpoint requirements: https://developer.sprucehealth.com/reference/webhooks-overview
- Register an endpoint: https://developer.sprucehealth.com/reference/createwebhookendpoint
- List/delete endpoints: https://developer.sprucehealth.com/reference/listwebhookendpoints and https://developer.sprucehealth.com/reference/deletewebhookendpoint
- Pause/resume delivery: https://developer.sprucehealth.com/reference/modifywebhookendpointpaused
- Delivery audit log: https://developer.sprucehealth.com/reference/listwebhookendpointevents
Event types most relevant to a bidirectional integration
- Contact (demographics):
contact.created,contact.updated,contact.deleted,contact.merged—data.objectis the full Contact resource, same shape as the REST API (sointegrationLinksare available on the payload). - Conversation (thread metadata):
conversation.created,conversation.updated,conversation.deleted—data.objectincludesassociatedContactIds,externalParticipants,tags,archived,assignedToMemberId. - Conversation item (messages, notes, calls, attachments):
conversationItem.created,conversationItem.updated,conversationItem.deleted,conversationItem.restored—data.objectembeds the parent conversation, so you rarely need a follow-up GET.
Recommended End-to-End Architecture
- Bootstrap: one-time backfill — for each active record in the external system,
POST /v1/contactsfor contacts that don't already exist, thenPOST /v1/contacts/{id}/integrationlinkswithtype=customand the external record URL. Persist the Spruce contact id on your side (optional if you rely onintegrationIDFilterfor lookups). - Outbound sync (external system → Spruce): when the external system emits a demographic change,
PATCHthe corresponding Spruce contact. Use a stable idempotency key derived from the external change id. - Inbound sync (Spruce → external system): register one webhook endpoint per environment. On each event:
- Verify the
X-Spruce-Signatureheader (see the webhooks overview linked above). - Ack with 2XX immediately; enqueue for async processing.
- Resolve the external record via the Integration Link on the contact payload (or via
associatedContactIds+integrationIDFilterfor conversation events). - Apply the demographic change, or record the conversation activity in the external system (e.g. as a communication note, task, or timeline entry).
- Verify the
Updated about 14 hours ago
