Building workflows on the API
Safe re-runs for automations: idempotent tasks, duplicate-proof purchase imports, the client history feed, and the headers that tag a workflow's changes.
Advanced An automation (n8n, Make, Zapier, your own script) runs again and again, and sometimes twice at once. These API features exist so a workflow can run as often as it likes without creating duplicates, reopening work a coach already closed, or losing track of what it did. Every endpoint below is in the API reference with full request and response shapes.
Tag every change with your workflow
Send two optional headers on any API call that changes something:
| Header | Value | Example |
|---|---|---|
X-Protocol-Automation-Id |
A UUID that identifies your workflow | 3f1c2b4a-5d6e-4f70-8a9b-0c1d2e3f4a5b |
X-Protocol-Evidence-Key |
What the change is based on, up to 255 characters, so your workflow can find it again | missed-checkins:2026-W40 |
X-Protocol-Reason |
Why, in words your coach reads, up to 500 characters. Percent-encode it (encodeURIComponent): header values cannot carry letters like č or ž directly |
Missed%20two%20check-ins%20in%20a%20row |
Protocol stores both with the change in the client history (below), so the workflow can later ask “what did I change, and why”. A value that is not a UUID, or a key longer than 255 characters, is ignored rather than failing the call.
Create a task only once
POST /v1/tasks accepts an externalKey, unique within your organization (up to 255 characters).
Make it name the thing the task is about, for example
flag:missed-checkins:<clientId>:<weekStart>.
- The first call creates the task and answers 201 with
"existing": false. - Every later call with the same key answers 200 with the task exactly as it is now and
"existing": true. Nothing in the request is applied: the task is not renamed, not moved, not reopened if a coach completed it, and nobody is notified again. - Two calls at the same moment still produce one task.
Find your tasks again with GET /v1/tasks?externalKey=<key> or
GET /v1/tasks?externalKeyPrefix=flag:missed-checkins:. The key is returned as externalKey on
every task.
Import a purchase only once
A purchase with an externalSource and externalId (for example "externalSource": "sheet",
"externalId": "row-17") is unique within your organization. Sending
POST /v1/shop/purchases again with the same pair does not create a second purchase. It answers
409 Conflict with the purchase that already exists:
{
"success": false,
"message": "A purchase with this externalSource and externalId already exists",
"data": { "existingPurchaseId": "e2653e9b-0adf-4e45-8ee5-a48b5b117435" }
}
Read or update that purchase with GET or PATCH /v1/shop/purchases/<existingPurchaseId>. The
same external id under a different externalSource is a different purchase.
Read the client history
GET /v1/client-history lists what changed on your clients and who changed it: lifecycle stage
moves (stage.changed), labels added and removed (label.added, label.removed), and a check-in
reminder’s assigned team member changing (reminder.assignee_changed). It is the memory a
workflow can rely on instead of its own last few runs.
A client’s history may also start with a stage.snapshot: the stage they were in when history
recording began (6 October 2026). It is not a change, so a workflow that counts stage moves should
ask for types=stage.changed.
| Query parameter | Meaning |
|---|---|
clientId |
One client |
types |
Comma-separated event types |
since, until |
ISO-8601 time window (since inclusive, until exclusive) |
automationId, evidenceKey |
Only changes made with those headers |
order |
asc (default) or desc |
limit |
Page size, 100 by default, up to 1000 |
cursor |
The nextCursor from the previous page |
Each event names the client, the label or reminder it was about, the old and new value, the names
at the time (so a later rename does not rewrite history), the person behind it, and how they
connected (session, apiKey, agent, or system for Protocol’s own jobs). A team member who
can only see their own clients sees only their clients’ history.
Events are stamped when the change is saved. When you poll forward with a cursor, re-read a short overlap and skip ids you have already seen.