> ## Documentation Index
> Fetch the complete documentation index at: https://docs.custral.com/llms.txt
> Use this file to discover all available pages before exploring further.

# task.completed

> A task was marked done, with the record it hangs off.

## Payload

```json theme={null}
{
  "id": "whd_2Xn8rK5cD",
  "event": "task.completed",
  "createdAt": "2026-09-11T14:44:19.903Z",
  "data": {
    "taskId": "task_6Rj2wF",
    "title": "Send the renewal quote",
    "recordId": "rec_3Ab9xK2mQ7"
  }
}
```

<ResponseField name="taskId" type="string" required>
  The task that was completed. Always present.
</ResponseField>

<ResponseField name="title" type="string">
  The task's title.
</ResponseField>

<ResponseField name="recordId" type="string">
  The record the task hangs off. **Omitted** when the task is attached to
  nothing, which is a normal state: not every task belongs to a record.
</ResponseField>

## Worth knowing

* **Completion only.** Creating, reassigning, re-opening and deleting a task do
  not emit a webhook event.
* **No assignee on the payload.** Read the task back if you need to know who
  closed it.
* **Setup tasks complete themselves.** A task that asks somebody to connect a
  mailbox is closed by the workspace the moment it can see the mailbox, rather
  than by a person ticking it, and that still fires this event. Do not read a
  completion as proof a human acted.

## Example

```ts theme={null}
custral.on("task.completed", async (event) => {
  const {taskId, title, recordId} = event.data;

  await ops.log({
    message: `Task closed: ${title ?? taskId}`,
    // `recordId` is absent for a standalone task.
    record: recordId ?? null,
  });
});
```


## Related topics

- [Tasks](/dev/webhooks/events/tasks.md)
- [Webhooks](/dev/webhooks/overview.md)
- [Event catalog](/dev/webhooks/events/overview.md)
- [Events](/dev/events.md)
- [Task Overview](/team/tasks/overview.md)
