Webhook Events
A webhook subscription listens for one of the nine events on this page. Each section says precisely what fires the event and what looks as if it should but does not, then gives the body CheckFlow posts. Subscribing, delivery, retries and signature verification are the same for every event and are described once, on Webhooks.
| Event | Fires when | Scopes | Payload |
|---|---|---|---|
new_checklist | A checklist is started from a template. | Template | Checklist |
task_completed | A task is completed. | Task, Template, Workspace | Task |
file_uploaded | A file is uploaded to a File Upload control. | Field | File |
checklist_completed | A checklist becomes complete. | Template, Workspace | Checklist |
comment_created | A comment is written on a task. | Task, Workspace | Comment |
task_assigned | A task is given to somebody who did not have it. | Task, Template, Workspace | Assignment |
data_set.record.created | A Data Set record is added. | Workspace | Data Set record |
data_set.record.updated | A Data Set record's values change. | Workspace | Data Set record |
data_set.record.deleted | A Data Set record is deleted. | Workspace | Data Set record |
A v2 subscription and a v3 subscription to the same event receive the same body: the payload is built once per event and posted to every matching subscription. The only difference is the CF-Signature header, which a subscription without a secret does not get.
Reading a Payload
The payloads come in two families, and they are formatted differently:
new_checklist,task_completed,file_uploadedandchecklist_completedsend the checklist, task and file shapes the v2 API has always sent. They are indented JSON, with property names prefixed by the object they belong to (checklistName,taskKey), and they contain noeventTypeproperty. Their timestamps are UTC written with an offset, such as2026-09-15T09:40:12+00:00. The offset is always+00:00: the services that send webhooks run on UTC in both regions. A property with no value is sent asnull, not left out.comment_created,task_assignedand the three Data Set events are single-line JSON that starts with aneventTypeproperty. A property with no value is sent asnull.
Every delivery also names its event in the CF-Event header — see Webhooks — which is the way to tell the four legacy payloads apart.
Both families are written with JSON escaping of characters outside ASCII and of HTML-sensitive characters: an em dash arrives as —, & as &, < and > as < and >, a double quote inside a string as ". Any JSON parser turns these back into the characters, but the escaped form is what the signature was computed over, so verify the raw bytes before parsing. The examples on this page show the payloads exactly as sent, except that single-line payloads are shown indented for reading.
This page does not follow the rest of the v3 API's conventions — camelCase names without prefixes, keys rather than numeric ids, timestamps ending in Z, absent rather than null — because the four older payloads cannot change without breaking the receivers built on them. Read a payload as a notification and use the keys in it with the v3 routes, such as GET /v3/checklists/{key}, when you need the full, current picture.
Shared Payload Objects
The Checklist Payload
new_checklist and checklist_completed send the whole checklist, with every task on it and each task's fields and comments.
| Field | Type | Description |
|---|---|---|
checklistId | integer | The checklist's numeric id. |
checklistKey | string (GUID) | The checklist's key — the one the v3 checklist routes take. |
checklistName | string | The checklist's name. |
checklistUrl | string | The checklist's page in the app. |
checklistScheduledDateTime | string or null | When a scheduled checklist was scheduled for; null otherwise. |
checklistStartDateTime | string or null | When the checklist was started. |
checklistEndDateTime | string or null | When the checklist was completed; null while it is open. |
checklistCreatedByName | string | Who started it. CheckFlow Scheduler for a checklist started by a schedule. |
checklistCreatedByEmail | string | Their email address. support@checkflow.io for a scheduled checklist. |
checklistIsShared | boolean | Whether the checklist is shared by link. |
checklistSharedUrl | string or null | The share link, or null when there is none. |
checklistIsArchived | boolean | Whether the checklist is archived. |
template | Template | The template it was started from. |
tasks | array of Checklist Task | Every task on the checklist, in order, headings included. |
Each entry in tasks:
| Field | Type | Description |
|---|---|---|
taskId | integer | The task's numeric id on this checklist. |
taskKey | string (GUID) | The task's key. This is the template task's key, so it is the same on every checklist started from the template — see Scope Keys Are Template Keys. |
taskName | string | The task's name. |
taskIsHeading | boolean | Whether the entry is a heading rather than a task. |
taskIsCurrentlyHalted | boolean | Whether the task is waiting on an earlier task. |
taskIsCurrentlyHidden | boolean | Whether conditional logic currently hides the task. |
taskDueDateTime | string or null | The task's due date. |
taskCompletedDateTime | string or null | When it was completed or marked Not Applicable. |
taskCompletedByName | string or null | Who completed it. |
taskCompletedByEmail | string or null | Their email address. |
assignees | array of Assignee | Who the task is assigned to. |
fields | array | The task's input controls and their values — see Fields in a Payload. |
comments | array of Comment | The comments on the task. |
A task on the checklist payload has no taskUrl and no tags; the Task Payload has both.
The Task Payload
task_completed sends one task, with its checklist and template.
| Field | Type | Description |
|---|---|---|
taskId | integer | The task's numeric id. |
taskKey | string (GUID) | The task's key — the template task's key for a checklist task, the task's own key for a standalone task. |
taskName | string | The task's name. |
taskUrl | string | The task in the app: its place on its checklist, or, for a standalone task that is not attached to a checklist, the task in the Tasks grid. |
taskIsHeading | boolean | Whether the task is a heading. |
taskIsCurrentlyHalted | boolean | Whether the task is waiting on an earlier task. |
taskIsCurrentlyHidden | boolean | Whether conditional logic currently hides the task. |
taskDueDateTime | string or null | The task's due date. |
taskCompletedDateTime | string or null | When it was completed. |
taskCompletedByName | string or null | Who completed it. |
taskCompletedByEmail | string or null | Their email address. |
assignees | array of Assignee | Who the task is assigned to. |
fields | array | The task's input controls and their values — see Fields in a Payload. |
comments | array of Comment | The comments on the task. |
tags | array of Tag | The task's tags. |
checklist | Checklist Summary | The checklist the task is on. |
template | Template | The checklist's template. |
A standalone task that is not attached to a checklist still has checklist and template objects, with every property present and empty: checklistId and templateId are 0, templateVersion is 0, checklistIsShared is false, and everything else is null. Test checklist.checklistKey for null to tell the two cases apart. A standalone task attached to a checklist reports that checklist and its template.
The Checklist Summary Object
The checklist object inside the task and file payloads. It has the first eleven properties of the Checklist Payload — checklistId to checklistSharedUrl — and not checklistIsArchived, template or tasks; the template is a sibling object instead.
The Template Object
| Field | Type | Description |
|---|---|---|
templateId | integer | The template's numeric id. |
templateKey | string (GUID) | The template's key — the one a Template scope names. |
templateName | string | The template's name. |
templateUrl | string | The template's page in the app. |
templateDescription | string | The template's description. |
templateVersion | integer | The template version the checklist was started from. |
templateCreatedDateTime | string | When that template version was created. |
The Assignee Object
| Field | Type | Description |
|---|---|---|
assigneeId | integer | The member's or group's id — the same ids as Members and Groups. |
assigneeType | string | TeamMember or Group. |
assigneeName | string | The member's or group's name. |
The Comment Object
| Field | Type | Description |
|---|---|---|
commentID | integer | The comment's id. Note the capital ID. |
commentHtml | string | The comment as HTML, as it was typed. |
commentDateTime | string | When it was written. |
The Tag Object
| Field | Type | Description |
|---|---|---|
tagKey | string | The tag's key. |
tagName | string | The tag's name. |
assignmentKey | string | The key of what the tag is on. |
assignmentType | string | Task. |
parentKey | string or null | For a task on a checklist, the checklist's key. For a standalone task, null. |
Fields in a Payload
Fields are the task's input controls (see Control Types). In these four payloads each field is an object with a single property, and the property's name is built from the control's type and its label: the type name below, an underscore, then the label, both lower-cased with spaces replaced by underscores. A Short Text control labelled Invoice Number becomes short_text_invoice_number. An unlabelled control is named from Unlabelled <type> Content at Position <n> in the same way.
| Control | Property name starts | Value |
|---|---|---|
| Short Text | short_text_ | The text, or null. |
| Long Text | long_text_ | The text, or null. |
email_input_ | The address, or null. | |
| Website | website_ | The URL, or null. |
| Date & Time | date_ | The date, or null. In the task payload it has an offset (2026-09-12T00:00:00+00:00); in the checklist payload it has none (2026-09-12T00:00:00). |
| Drop-Down | drop_down_ | The selected option's text, or null. |
| Multi-Choice | multi_choice_drop_down_ | An array with one object per option, each holding one property — the field's name, _, and the option's text in the same form — set to true or false. |
| Sub-Tasks | sub_tasks_ | An array with one object per sub-task, named in the same way as Multi-Choice options, set to whether it is ticked. |
| File Upload | file_ | An array with one object per uploaded file, holding <name>_file_<n>_name, <name>_file_<n>_content_type and <name>_file_<n>_url, where <n> counts from 0. |
| File | file_upload_ | The same array, for the files attached in the template. |
| Table | table_ | An array with one object per filled cell: rowIndex, columnIndex (both from 1), value and isReadOnly. |
| Mail To | — | An empty object {}. |
Other controls — including Members, Linked Checklist and the content controls — do not appear in fields.
The property names change when somebody edits a control's label or an option's text, and a label with punctuation keeps it (Invoice # becomes short_text_invoice_#). Two controls with the same type and label on one task produce the same name. If you need a stable reference to a field, read the task through Task Fields, which identifies fields by key.
new_checklist
Fires when a checklist is started from a template. A typical receiver opens a matching record in another system.
new_checklist
When It Fires
Once for each checklist started from the template the subscription is scoped to, however it was started: in the app, through the v2 or v3 API or the MCP server, by a schedule, or by a Linked Checklist control creating one automatically. Checklists created many at a time — for example with the app's Create Multiple Checklists tool — each fire their own event, just as if they had been started one by one.
It does not fire when an existing checklist is upgraded to a newer version of its template. It is about a template only: there is no workspace-wide form, so to hear about every new checklist you need one subscription per template.
Payload Schema
The Checklist Payload, as it stands when the event is processed — usually moments after the checklist was created. checklistEndDateTime is null and most fields are empty, apart from any values filled in when the checklist was created.
Example Payload
{
"checklistId": 48213,
"checklistKey": "c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54",
"checklistName": "Invoice Review — INV-2041",
"checklistUrl": "https://app.checkflow.io/Checklist/Index?checklistKey=c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54",
"checklistScheduledDateTime": null,
"checklistStartDateTime": "2026-09-15T08:58:31+00:00",
"checklistEndDateTime": null,
"checklistCreatedByName": "Priya Patel",
"checklistCreatedByEmail": "priya.patel@acme.example",
"checklistIsShared": false,
"checklistSharedUrl": null,
"checklistIsArchived": false,
"template": {
"templateId": 312,
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"templateName": "Invoice Review",
"templateUrl": "https://app.checkflow.io/Template/Index?templateId=312",
"templateDescription": "Check, approve and file a supplier invoice.",
"templateVersion": 4,
"templateCreatedDateTime": "2026-02-03T10:15:00+00:00"
},
"tasks": [
{
"taskId": 901,
"taskKey": "a41f6b2d-9c3e-4d7a-8b15-6e0f2c9d3a58",
"taskName": "Check invoice details",
"taskIsHeading": false,
"taskIsCurrentlyHalted": false,
"taskIsCurrentlyHidden": false,
"taskDueDateTime": "2026-09-17T17:00:00+00:00",
"taskCompletedDateTime": null,
"taskCompletedByName": null,
"taskCompletedByEmail": null,
"assignees": [
{
"assigneeId": 12,
"assigneeType": "Group",
"assigneeName": "Finance Team"
}
],
"fields": [
{
"short_text_invoice_number": "INV-2041"
},
{
"date_invoice_date": null
},
{
"file_signed_invoice": []
}
],
"comments": []
},
{
"taskId": 902,
"taskKey": "6d3c9e1f-4a7b-4e52-b0d8-2c5f9a1e7b36",
"taskName": "Approve invoice",
"taskIsHeading": false,
"taskIsCurrentlyHalted": true,
"taskIsCurrentlyHidden": false,
"taskDueDateTime": null,
"taskCompletedDateTime": null,
"taskCompletedByName": null,
"taskCompletedByEmail": null,
"assignees": [
{
"assigneeId": 1041,
"assigneeType": "TeamMember",
"assigneeName": "Sarah Chen"
}
],
"fields": [
{
"drop_down_decision": null
},
{
"sub_tasks_checks": [
{
"sub_tasks_checks_po_matched": false
},
{
"sub_tasks_checks_vat_correct": false
}
]
}
],
"comments": []
}
]
}
Delivery and Retries
See Receiving Deliveries. The body grows with the number of tasks and fields on the template.
Verification
Signed with CF-Signature when the subscription has a secret — see Verifying Signatures. A subscription without a secret receives no signature, and nothing in the delivery proves it came from CheckFlow; confirm the checklist with GET /v3/checklists/{key} before acting on it. The body has no eventType and is the same shape as checklist_completed, so give this subscription a target URL of its own.
Subscribing
POST https://api.checkflow.io/v3/webhooks
X-API-KEY: your-api-key-here
Content-Type: application/json
{
"eventType": "new_checklist",
"targetUrl": "https://hooks.acme.example/checkflow/new-checklist",
"scope": {
"type": "Template",
"key": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14"
}
}
See Create a Webhook.
Related Events
- checklist_completed — the other end of the checklist's life, with the same payload shape.
- task_assigned — fires separately for the people given tasks on the new checklist.
task_completed
Fires when a task is completed. A typical receiver moves something on in another system when a particular step is done.
task_completed
When It Fires
Each time a task is completed, in the app, through the v2 or v3 API or through the MCP server — checklist tasks and standalone tasks alike. Completing a task, un-completing it and completing it again fires twice.
Which subscriptions receive it depends on scope:
| Scope | Receives |
|---|---|
Task with a template task's key | That task, completed on any checklist started from the template. |
Task with a standalone task's key | That one standalone task. |
Template | Every task completed on a checklist started from the template, including standalone tasks attached to such a checklist. |
Workspace | Every task completed anywhere in the workspace, including standalone tasks. |
A subscription that matches in more than one way receives the event once.
It does not fire when:
- a task is marked Not Applicable — even though that stamps the task's completion time;
- a task is un-completed — there is no
task_uncompletedevent; - a checklist is upgraded to a newer template version and the tasks that were already complete are carried across;
- the task has been un-completed again by the time the event is processed, which is normally within moments. The event is dropped rather than sent about a task that is no longer complete.
A standalone task that is not attached to a checklist has no template, so a Template-scoped subscription never receives it.
Payload Schema
The Task Payload, as the task stands when the event is processed.
Example Payload
{
"taskId": 902,
"taskKey": "6d3c9e1f-4a7b-4e52-b0d8-2c5f9a1e7b36",
"taskName": "Approve invoice",
"taskUrl": "https://app.checkflow.io/Checklist/Index?checklistKey=c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54&checklistTaskId=902",
"taskIsHeading": false,
"taskIsCurrentlyHalted": false,
"taskIsCurrentlyHidden": false,
"taskDueDateTime": "2026-09-18T17:00:00+00:00",
"taskCompletedDateTime": "2026-09-15T09:40:12+00:00",
"taskCompletedByName": "Sarah Chen",
"taskCompletedByEmail": "sarah.chen@acme.example",
"assignees": [
{
"assigneeId": 1041,
"assigneeType": "TeamMember",
"assigneeName": "Sarah Chen"
}
],
"fields": [
{
"drop_down_decision": "Approved"
},
{
"sub_tasks_checks": [
{
"sub_tasks_checks_po_matched": true
},
{
"sub_tasks_checks_vat_correct": true
}
]
}
],
"comments": [
{
"commentID": 5521,
"commentHtml": "<p>Approved for payment.</p>",
"commentDateTime": "2026-09-15T09:39:50+00:00"
}
],
"tags": [
{
"tagKey": "e4c1a7f2-8b3d-4a95-9c60-1f2e7d8b5a36",
"tagName": "urgent",
"assignmentKey": "6d3c9e1f-4a7b-4e52-b0d8-2c5f9a1e7b36",
"assignmentType": "Task",
"parentKey": "c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54"
}
],
"checklist": {
"checklistId": 48213,
"checklistKey": "c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54",
"checklistName": "Invoice Review — INV-2041",
"checklistUrl": "https://app.checkflow.io/Checklist/Index?checklistKey=c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54",
"checklistScheduledDateTime": null,
"checklistStartDateTime": "2026-09-15T08:58:31+00:00",
"checklistEndDateTime": null,
"checklistCreatedByName": "Priya Patel",
"checklistCreatedByEmail": "priya.patel@acme.example",
"checklistIsShared": false,
"checklistSharedUrl": null
},
"template": {
"templateId": 312,
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"templateName": "Invoice Review",
"templateUrl": "https://app.checkflow.io/Template/Index?templateId=312",
"templateDescription": "Check, approve and file a supplier invoice.",
"templateVersion": 4,
"templateCreatedDateTime": "2026-02-03T10:15:00+00:00"
}
}
checklistEndDateTime is null here even though this was the last task: the checklist's completion is a separate event, checklist_completed.
Delivery and Retries
See Receiving Deliveries. A Workspace subscription on a busy workspace receives one delivery per completed task, so completing many tasks at once produces a burst.
Verification
Signed with CF-Signature when the subscription has a secret — see Verifying Signatures. Without a secret, confirm the task through the API before acting on it. The body has no eventType.
Subscribing
POST https://api.checkflow.io/v3/webhooks
X-API-KEY: your-api-key-here
Content-Type: application/json
{
"eventType": "task_completed",
"targetUrl": "https://hooks.acme.example/checkflow/invoice-approved",
"scope": {
"type": "Task",
"key": "6d3c9e1f-4a7b-4e52-b0d8-2c5f9a1e7b36"
}
}
See Create a Webhook.
Related Events
- checklist_completed — fires as well when the completed task was the checklist's last outstanding one.
- file_uploaded — fires for files as they are uploaded, before the task is completed.
file_uploaded
Fires when a file is uploaded to a File Upload control. A typical receiver copies the file into a document store.
file_uploaded
When It Fires
Once for each file uploaded to the File Upload control the subscription is scoped to, on any checklist started from its template, whether uploaded in the app or through the v3 API or MCP server. Uploading three files fires three events.
It does not fire when:
- a file is attached to a comment;
- a file is removed from the control;
- the file has already been removed by the time the event is processed — the event is dropped.
It is about one control only. There is no workspace-wide or template-wide form, and the scope key must be the key of a File Upload control — the key of any other control is refused.
Payload Schema
| Field | Type | Description |
|---|---|---|
id | integer | The File Upload control's numeric id on this checklist. |
fileUploadKey | string (GUID) | The control's key — the same template-level key the subscription's Field scope names. |
fileUploadLabel | string | The control's label. |
minFilesRequired | integer | The control's minimum number of files. |
maxFilesRequired | integer | The control's maximum number of files. |
files | array | The file that was uploaded — always exactly one entry. |
files[].fileId | integer | The file's id. |
files[].originalFileName | string | The file's name as uploaded. |
files[].fileType | string | The file's content type. |
files[].url | string | The file's address in storage. It can be downloaded without authentication and carries no expiring token, so it works for as long as the file is kept. Each workspace's files are held in a storage container of its own, and every stored file name starts with a GUID, so the address cannot be guessed — but anyone who has it can download the file. Treat it as confidential. |
files[].uploadedById | integer or null | The id of the member who uploaded it. |
files[].uploadedByName | string or null | Their name. |
files[].uploadedByEmail | string or null | Their email address. |
files[].uploadedDateTime | string or null | When it was uploaded. |
task | object | The task the control is on: taskId, taskKey, taskName, taskUrl, taskDueDateTime, taskCompletedDateTime, taskCompletedByName and taskCompletedByEmail, as on the Task Payload. |
checklist | Checklist Summary | The checklist. Empty, as described under the Task Payload, for an unattached standalone task. |
template | Template | The checklist's template. Empty in the same case. |
Example Payload
{
"id": 7310,
"fileUploadKey": "5e9b2c71-0d4a-4f86-a3c2-8b17e6d0f934",
"fileUploadLabel": "Signed invoice",
"minFilesRequired": 1,
"maxFilesRequired": 3,
"files": [
{
"fileId": 66120,
"originalFileName": "INV-2041-signed.pdf",
"fileType": "application/pdf",
"url": "https://storage.example/6c1d2f0a-93b4-4e7d-a8f2-51c0de7b9a33/0f3c9e2a-8b41-4d7e-a5c2-9e1b7d4f6a30_INV-2041-signed.pdf",
"uploadedById": 1043,
"uploadedByName": "James Okafor",
"uploadedByEmail": "james.okafor@acme.example",
"uploadedDateTime": "2026-09-15T09:05:48+00:00"
}
],
"task": {
"taskId": 901,
"taskKey": "a41f6b2d-9c3e-4d7a-8b15-6e0f2c9d3a58",
"taskName": "Check invoice details",
"taskUrl": "https://app.checkflow.io/Checklist/Index?checklistKey=c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54&checklistTaskId=901",
"taskDueDateTime": "2026-09-17T17:00:00+00:00",
"taskCompletedDateTime": null,
"taskCompletedByName": null,
"taskCompletedByEmail": null
},
"checklist": {
"checklistId": 48213,
"checklistKey": "c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54",
"checklistName": "Invoice Review — INV-2041",
"checklistUrl": "https://app.checkflow.io/Checklist/Index?checklistKey=c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54",
"checklistScheduledDateTime": null,
"checklistStartDateTime": "2026-09-15T08:58:31+00:00",
"checklistEndDateTime": null,
"checklistCreatedByName": "Priya Patel",
"checklistCreatedByEmail": "priya.patel@acme.example",
"checklistIsShared": false,
"checklistSharedUrl": null
},
"template": {
"templateId": 312,
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"templateName": "Invoice Review",
"templateUrl": "https://app.checkflow.io/Template/Index?templateId=312",
"templateDescription": "Check, approve and file a supplier invoice.",
"templateVersion": 4,
"templateCreatedDateTime": "2026-02-03T10:15:00+00:00"
}
}
Delivery and Retries
See Receiving Deliveries. The payload carries a link to the file, not the file itself.
Verification
Signed with CF-Signature when the subscription has a secret — see Verifying Signatures. Without a secret, do not fetch a URL from an unverified payload; read the file through the API instead. The body has no eventType.
Subscribing
POST https://api.checkflow.io/v3/webhooks
X-API-KEY: your-api-key-here
Content-Type: application/json
{
"eventType": "file_uploaded",
"targetUrl": "https://hooks.acme.example/checkflow/signed-invoice",
"scope": {
"type": "Field",
"key": "5e9b2c71-0d4a-4f86-a3c2-8b17e6d0f934"
}
}
See Create a Webhook.
Related Events
- task_completed — fires when the task holding the control is completed, with every file on it in
fields.
checklist_completed
Fires when a checklist becomes complete. A typical receiver closes the matching record in another system.
checklist_completed
When It Fires
When a checklist's end date is set — the one moment a checklist becomes complete, however it got there: its last outstanding task was completed, a standalone task holding it up was detached, or it was marked complete in the app or through the API.
It does not fire when a checklist is reopened; there is no checklist_uncompleted event. A checklist that is reopened and completed again fires a second time. A checklist with no tasks other than headings is never counted as complete by attaching or detaching tasks.
Payload Schema
The Checklist Payload, with checklistEndDateTime set. It is the same shape as new_checklist.
Example Payload
{
"checklistId": 48213,
"checklistKey": "c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54",
"checklistName": "Invoice Review — INV-2041",
"checklistUrl": "https://app.checkflow.io/Checklist/Index?checklistKey=c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54",
"checklistScheduledDateTime": null,
"checklistStartDateTime": "2026-09-15T08:58:31+00:00",
"checklistEndDateTime": "2026-09-15T09:40:12+00:00",
"checklistCreatedByName": "Priya Patel",
"checklistCreatedByEmail": "priya.patel@acme.example",
"checklistIsShared": false,
"checklistSharedUrl": null,
"checklistIsArchived": false,
"template": {
"templateId": 312,
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"templateName": "Invoice Review",
"templateUrl": "https://app.checkflow.io/Template/Index?templateId=312",
"templateDescription": "Check, approve and file a supplier invoice.",
"templateVersion": 4,
"templateCreatedDateTime": "2026-02-03T10:15:00+00:00"
},
"tasks": [
{
"taskId": 901,
"taskKey": "a41f6b2d-9c3e-4d7a-8b15-6e0f2c9d3a58",
"taskName": "Check invoice details",
"taskIsHeading": false,
"taskIsCurrentlyHalted": false,
"taskIsCurrentlyHidden": false,
"taskDueDateTime": "2026-09-17T17:00:00+00:00",
"taskCompletedDateTime": "2026-09-15T09:07:03+00:00",
"taskCompletedByName": "James Okafor",
"taskCompletedByEmail": "james.okafor@acme.example",
"assignees": [
{
"assigneeId": 12,
"assigneeType": "Group",
"assigneeName": "Finance Team"
}
],
"fields": [
{
"short_text_invoice_number": "INV-2041"
},
{
"date_invoice_date": "2026-09-12T00:00:00"
},
{
"file_signed_invoice": [
{
"file_signed_invoice_file_0_name": "INV-2041-signed.pdf",
"file_signed_invoice_file_0_content_type": "application/pdf",
"file_signed_invoice_file_0_url": "https://files.example/acme/66120/INV-2041-signed.pdf"
}
]
}
],
"comments": []
},
{
"taskId": 902,
"taskKey": "6d3c9e1f-4a7b-4e52-b0d8-2c5f9a1e7b36",
"taskName": "Approve invoice",
"taskIsHeading": false,
"taskIsCurrentlyHalted": false,
"taskIsCurrentlyHidden": false,
"taskDueDateTime": "2026-09-18T17:00:00+00:00",
"taskCompletedDateTime": "2026-09-15T09:40:12+00:00",
"taskCompletedByName": "Sarah Chen",
"taskCompletedByEmail": "sarah.chen@acme.example",
"assignees": [
{
"assigneeId": 1041,
"assigneeType": "TeamMember",
"assigneeName": "Sarah Chen"
}
],
"fields": [
{
"drop_down_decision": "Approved"
},
{
"sub_tasks_checks": [
{
"sub_tasks_checks_po_matched": true
},
{
"sub_tasks_checks_vat_correct": true
}
]
}
],
"comments": [
{
"commentID": 5521,
"commentHtml": "<p>Approved for payment.</p>",
"commentDateTime": "2026-09-15T09:39:50+00:00"
}
]
}
]
}
Delivery and Retries
See Receiving Deliveries. When the last task is completed, task_completed and checklist_completed are separate events and can arrive in either order.
Verification
Signed with CF-Signature when the subscription has a secret — see Verifying Signatures. Without a secret, confirm the checklist through the API before acting on it. The body has no eventType and is the same shape as new_checklist, so give this subscription a target URL of its own; a non-null checklistEndDateTime is the only thing in the body that tells the two apart.
Subscribing
POST https://api.checkflow.io/v3/webhooks
X-API-KEY: your-api-key-here
Content-Type: application/json
{
"eventType": "checklist_completed",
"targetUrl": "https://hooks.acme.example/checkflow/invoice-review-done",
"scope": {
"type": "Template",
"key": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14"
}
}
Leave out scope to receive every checklist completed in the workspace. A Task scope is refused.
Related Events
- new_checklist — the same payload when the checklist is started.
- task_completed — fires for each task, including the last one.
comment_created
Fires when somebody writes a comment on a task. A typical receiver copies the comment into a ticket or a chat channel.
comment_created
When It Fires
Once for each new comment on a checklist task or a standalone task, whether written in the app, through the v2 or v3 API or through the MCP server. A Task scope with a template task's key receives comments on that task on every checklist started from the template; with a standalone task's key, comments on that task. A Workspace scope receives every comment in the workspace.
It does not fire when a comment is edited or deleted. A Template scope is refused for this event, because a comment does not carry its template.
Payload Schema
| Field | Type | Description |
|---|---|---|
eventType | string | comment_created. |
commentId | integer | The comment's id. There is no GUID key for a comment. |
createdDateTime | string | When the comment was written, in UTC, ending in Z, such as 2026-09-15T09:39:50.217Z. |
createdById | integer | The id of the member who wrote it. |
createdByName | string or null | Their full name. |
createdByEmail | string or null | Their email address. |
html | string | The comment as HTML, exactly as it was typed. |
task | object | The task: id (integer), key (GUID — the template task's key, or a standalone task's own key) and name. |
checklist | object or null | The checklist: id, key and name. null for a standalone task that is not attached to a checklist. |
html is user-written markup going into your system. Sanitise it before displaying it anywhere.
Example Payload
Sent as one line; shown indented here.
{
"eventType": "comment_created",
"commentId": 5521,
"createdDateTime": "2026-09-15T09:39:50.217Z",
"createdById": 1041,
"createdByName": "Sarah Chen",
"createdByEmail": "sarah.chen@acme.example",
"html": "<p>Approved for payment.</p>",
"task": {
"id": 902,
"key": "6d3c9e1f-4a7b-4e52-b0d8-2c5f9a1e7b36",
"name": "Approve invoice"
},
"checklist": {
"id": 48213,
"key": "c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54",
"name": "Invoice Review — INV-2041"
}
}
Delivery and Retries
See Receiving Deliveries. A workspace-wide subscription receives every comment anybody writes.
Verification
Signed with CF-Signature when the subscription has a secret — see Verifying Signatures. This event can only be subscribed to through v3, but a subscription created over MCP has no secret you can see until it is rotated.
Subscribing
POST https://api.checkflow.io/v3/webhooks
X-API-KEY: your-api-key-here
Content-Type: application/json
{
"eventType": "comment_created",
"targetUrl": "https://hooks.acme.example/checkflow/approval-comments",
"scope": {
"type": "Task",
"key": "6d3c9e1f-4a7b-4e52-b0d8-2c5f9a1e7b36"
}
}
See Create a Webhook.
Related Events
- task_completed — its payload includes the task's comments so far, as
comments.
task_assigned
Fires when a task is given to somebody who did not have it. A typical receiver notifies that person in another system.
task_assigned
When It Fires
Once for each person newly responsible for an open task. Assigning a group sends one event for each of its members who did not already have the task; assigning two people sends two events. It fires wherever the assignment was made — in the app, through the API or MCP server — and for checklist tasks and standalone tasks alike.
It does not fire when:
- the assignees are saved again unchanged;
- somebody is removed from a task — there is no
task_unassignedevent; - the task is already complete;
- somebody already had the task through a group and is now assigned directly, or the other way round.
It fires for a deactivated member, although they are not sent the usual assignment email.
It does not fire for the assignments a checklist starts with. The tasks a template assigns are part of the new checklist: subscribe to new_checklist to hear about them, and the assignees are sent their "tasks assigned to you" email or Slack message when the new checklist is processed. It does fire when a checklist is upgraded to a newer template version that assigns a task to somebody who did not have it.
Payload Schema
| Field | Type | Description |
|---|---|---|
eventType | string | task_assigned. |
assignedDateTime | string | When the assignment was processed, in UTC, ending in Z. |
assignedToId | integer | The id of the member now responsible. |
assignedToName | string or null | Their full name. |
assignedToEmail | string or null | Their email address. |
task | object | id (integer), key (GUID), name, and dueDateTime — the due date in UTC, ending in Z, or null. |
checklist | object or null | id, key, name and template (key and name). null for a standalone task that is not attached to a checklist. |
The payload does not include the task's fields, so no form answers leave the workspace with it.
Example Payload
Sent as one line; shown indented here.
{
"eventType": "task_assigned",
"assignedDateTime": "2026-09-15T09:12:44.1873526Z",
"assignedToId": 1043,
"assignedToName": "James Okafor",
"assignedToEmail": "james.okafor@acme.example",
"task": {
"id": 901,
"key": "a41f6b2d-9c3e-4d7a-8b15-6e0f2c9d3a58",
"name": "Check invoice details",
"dueDateTime": "2026-09-17T17:00:00Z"
},
"checklist": {
"id": 48213,
"key": "c7a91e3d-5f2b-4c8e-a6d1-9b0f3e7c2a54",
"name": "Invoice Review — INV-2041",
"template": {
"key": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"name": "Invoice Review"
}
}
}
Delivery and Retries
See Receiving Deliveries. Assigning a large group sends one delivery per member.
Verification
Signed with CF-Signature when the subscription has a secret — see Verifying Signatures.
Subscribing
POST https://api.checkflow.io/v3/webhooks
X-API-KEY: your-api-key-here
Content-Type: application/json
{
"eventType": "task_assigned",
"targetUrl": "https://hooks.acme.example/checkflow/assignments",
"scope": {
"type": "Template",
"key": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14"
}
}
It accepts the same three scopes as task_completed. A standalone task that is not attached to a checklist has no template, so a Template scope never receives it.
Related Events
- task_completed — fires when the assignee finishes the task.
- new_checklist — fires when the checklist the task is on is started.
data_set.record.created
Fires when a record is added to a Data Set. A typical receiver mirrors the record into another system.
data_set.record.created
When It Fires
Once for each record added one at a time — on the Data Set's page in the app, or through the single-record create route of the v2 or v3 API or the MCP server (create_data_set_record). It fires for every Data Set in the workspace; the three Data Set events accept only a Workspace scope, so filter on dataSetId at your end.
It does not fire for records added in bulk: the bulk create routes of the v2 and v3 API, the MCP server's create_data_set_records, and every CSV import — creating a Data Set from a file or replacing its records from one, in the app or through the API — add their records without raising any event. Loading ten thousand records would otherwise deliver ten thousand events. If you mirror a Data Set that is loaded in bulk, read its records again after each load rather than relying on this event.
Payload Schema
| Field | Type | Description |
|---|---|---|
eventType | string | data_set.record.created. |
dataSetId | string (GUID) | The Data Set's key — the one the v3 Data Set routes take. |
dataSetName | string | The Data Set's name. |
recordId | string (GUID) | The new record's key. |
values | object | The record's values, keyed by field key. Every value is a string. A field with no value is left out. |
Example Payload
Sent as one line; shown indented here.
{
"eventType": "data_set.record.created",
"dataSetId": "9a3b6f88-1f2c-4a7d-9c62-1d7f5b0e4a11",
"dataSetName": "Suppliers",
"recordId": "c81f4a90-6f2b-4b8e-9a71-5d3c0e8b7a24",
"values": {
"4c2e0b1f-9a34-4c7e-b8d2-77f1a3e05c66": "Northwind Traders",
"8b71d2c5-4e19-42a3-9f0b-6c1e8d4a2b93": "EMEA",
"2f6a9d13-5b8e-4c07-a1d4-93e2c7b0f865": "true"
}
}
Look up what each field key means with GET /v3/data-sets/{dataSetKey}.
Delivery and Retries
See Receiving Deliveries.
Verification
Signed with CF-Signature when the subscription has a secret — see Verifying Signatures. Without a secret, read the record back through the API before acting on it.
Subscribing
POST https://api.checkflow.io/v3/webhooks
X-API-KEY: your-api-key-here
Content-Type: application/json
{
"eventType": "data_set.record.created",
"targetUrl": "https://hooks.acme.example/checkflow/supplier-added"
}
No scope: the Data Set events are workspace-wide only.
Related Events
- data_set.record.updated — fires when the record's values change afterwards.
- data_set.record.deleted — fires when it is removed.
data_set.record.updated
Fires when a Data Set record's values change.
data_set.record.updated
When It Fires
When one record is saved with at least one value different from before — on the Data Set's page in the app, or through the single-record update route of the v2 or v3 API or the MCP server. It fires for every Data Set in the workspace.
It does not fire when a record is saved with no actual change. A CSV replace does not update records either: it removes them and adds new ones under new keys, and raises no event at all. Changing a field's type to boolean rewrites stored words such as yes to true without raising this event, because the values mean what they meant.
Payload Schema
| Field | Type | Description |
|---|---|---|
eventType | string | data_set.record.updated. |
dataSetId | string (GUID) | The Data Set's key. |
dataSetName | string | The Data Set's name. |
recordId | string (GUID) | The record's key. |
changes | object | Only the fields that changed, keyed by field key. Each holds before and after, both strings, and null when the field was or is now empty. |
Example Payload
Sent as one line; shown indented here.
{
"eventType": "data_set.record.updated",
"dataSetId": "9a3b6f88-1f2c-4a7d-9c62-1d7f5b0e4a11",
"dataSetName": "Suppliers",
"recordId": "c81f4a90-6f2b-4b8e-9a71-5d3c0e8b7a24",
"changes": {
"8b71d2c5-4e19-42a3-9f0b-6c1e8d4a2b93": {
"before": "EMEA",
"after": "AMER"
},
"d05e7a3c-1f9b-4e26-8c4a-6b2f0e9d7a18": {
"before": null,
"after": "accounts@northwind.example"
}
}
}
Delivery and Retries
See Receiving Deliveries.
Verification
Signed with CF-Signature when the subscription has a secret — see Verifying Signatures.
Subscribing
POST https://api.checkflow.io/v3/webhooks
X-API-KEY: your-api-key-here
Content-Type: application/json
{
"eventType": "data_set.record.updated",
"targetUrl": "https://hooks.acme.example/checkflow/supplier-changed"
}
Related Events
- data_set.record.created — carries the record's full set of values; this event carries only the differences.
data_set.record.deleted
Fires when a Data Set record is deleted.
data_set.record.deleted
When It Fires
Once for each record deleted — one at a time or in bulk, on the Data Set's page in the app or through the delete routes of the v2 or v3 API or the MCP server. A bulk delete raises one event for each record it removes. It fires for every Data Set in the workspace.
It does not fire for the records removed when a bulk create or a CSV import replaces a Data Set's records — those writes raise no events at all — and it does not fire when a whole Data Set is deleted.
Payload Schema
| Field | Type | Description |
|---|---|---|
eventType | string | data_set.record.deleted. |
dataSetId | string (GUID) | The Data Set's key. |
dataSetName | string | The Data Set's name. |
recordId | string (GUID) | The deleted record's key. |
The record's values are not included; keep your own copy if you need them.
Example Payload
Sent exactly as shown, on one line:
{"eventType":"data_set.record.deleted","dataSetId":"9a3b6f88-1f2c-4a7d-9c62-1d7f5b0e4a11","dataSetName":"Suppliers","recordId":"c81f4a90-6f2b-4b8e-9a71-5d3c0e8b7a24"}
This is the body used in the worked signature example.
Delivery and Retries
See Receiving Deliveries. Deleting many records at once sends one delivery per record.
Verification
Signed with CF-Signature when the subscription has a secret — see Verifying Signatures. An unverified delete notification should not delete anything at your end until the record's absence is confirmed through the API.
Subscribing
POST https://api.checkflow.io/v3/webhooks
X-API-KEY: your-api-key-here
Content-Type: application/json
{
"eventType": "data_set.record.deleted",
"targetUrl": "https://hooks.acme.example/checkflow/supplier-deleted"
}
Related Events
- data_set.record.created — the record's values when it was added.
Related Pages
- Webhooks — creating subscriptions, delivery and retries, and verifying
CF-Signature. - Task Fields — reading a task's field values by key rather than by the generated names in these payloads.
- Data Sets — resolving the field keys in a Data Set payload.
- Control Types — the controls whose values appear in
fields.