The Task object
The following fields are accepted on write via POST /api/v1/tasks.
A task is also how calendar appointments work in agentlyleads: a timed task — one whose dueDate carries a clock time — shows up on the CRM calendar as an appointment. An all-day task — a bare date, or no dueDate at all — is a plain to-do with no clock time.
| Field | Type | Notes |
|---|---|---|
id | string | CUID, assigned on create (read-only) |
title | string (required) | Max 200 characters. What the task/appointment is |
dueDate | string | null | When it's due/starts. Three accepted shapes — see below |
allDay | boolean | Normally inferred from dueDate's shape; set to override |
endAt | string | null | When it stops (same shapes as dueDate). Only meaningful on a timed entry; must be after dueDate |
description | string | null | Max 5,000 characters. Free text — agenda, call notes, etc. |
type | enum | TODO (default) · CALL · EMAIL · MEETING — use MEETING for an appointment |
priority | enum | LOW · MEDIUM (default) · HIGH |
done | boolean | Defaults to false |
externalId | string | null | Your system's ID — upsert key and dedupe handle (max 200 chars) |
contactId | string | null | Link to an existing contact by its agentlyleads record ID |
leadId | string | null | Link to an existing lead by its agentlyleads record ID |
dealId | string | null | Link to an existing deal by its agentlyleads record ID |
contactExternalId | string | null | Resolve the linked contact by its externalId instead of contactId (max 200 chars) |
leadExternalId | string | null | Resolve the linked lead by its externalId instead of leadId (max 200 chars) |
dealExternalId | string | null | Resolve the linked deal by its externalId instead of dealId (max 200 chars) |
assigneeId | string | null | Workspace user to assign to, by user ID |
assigneeEmail | string | null | Resolve the assignee by their workspace login email instead of assigneeId |
createdAt | string (ISO 8601) | Assigned on create (read-only) |
updatedAt | string (ISO 8601) | Updated on every write (read-only) |
The dueDate/endAt shapes
This is the part that's easy to get wrong, so read it before you send your first booking. dueDate and endAt each accept three shapes, and the shape you send controls how the value is read:
1. A bare date — "YYYY-MM-DD", e.g. "2026-09-10".
An all-day entry, no clock time. This is a plain to-do.
2. A datetime with no timezone offset — e.g. "2026-09-10T14:00:00" or "2026-09-10T14:00".
Read as wall-clock time in the workspace's own timezone. This is what turns the row into a timed appointment that shows on the calendar with a clock time.
Do not send Z or a +HH:MM offset for this shape, or it will be read as that exact UTC/offset instant instead of the workspace's local time — almost never what you want for a booking made in local time.
3. A datetime with a Z or offset — e.g. "2026-09-10T18:00:00Z".
Treated as an exact instant and converted for display. Use this only when you deliberately already have a UTC/offset timestamp — not for a wall-clock booking time.
Omit dueDate (or send null) for no due date.
allDay and endAt
allDay is normally inferred from dueDate's shape above — you don't need to set it. Set it explicitly only to override that inference.
endAt is only meaningful on a timed entry: it turns a 2pm call into a 2–3pm meeting. On an all-day entry it's ignored/cleared. When present, endAt must be after dueDate.
Example object — a timed appointment
{
"id": "cmq7task0alk34567890",
"title": "Discovery call with Acme",
"done": false,
"dueDate": "2026-09-10T14:00:00.000Z",
"allDay": false,
"endAt": "2026-09-10T14:30:00.000Z",
"description": null,
"type": "MEETING",
"priority": "MEDIUM",
"externalId": "booking_9F3K2",
"contact": { "name": "Jane Buyer", "externalId": "hs_contact_123" },
"lead": null,
"deal": null,
"createdAt": "2025-06-01T12:00:00.000Z",
"updatedAt": "2025-06-01T12:00:00.000Z"
}dueDate/endAt are always returned as full ISO 8601 datetimes on read, regardless of the shape you wrote them in.
Notes on specific fields
title is the only required field. All other fields are optional on write.
externalId is the upsert key. A row with an externalId that matches an existing task in the workspace updates that task instead of creating a duplicate. It's also required to later reference this task from GET/DELETE by external ID — strongly recommended for anything you might need to update or cancel later, like a booking-site appointment.
Linking a contact, lead, or deal can be done either by agentlyleads record ID (contactId/leadId/dealId) or by an external system's ID (contactExternalId/leadExternalId/dealExternalId), which is resolved against that record's own externalId. If an *ExternalId is supplied but no matching record exists in the workspace, that row fails — it does not fall back to creating an unlinked task.
assigneeId/assigneeEmail picks which workspace user (sales rep or staff member) the appointment is assigned to. Omit both to leave it unassigned.
Recurring/repeating appointments are not supported via this endpoint — each occurrence must be created as its own row. Recurrence exists in the product via the MCP tools schedule_appointment/manage_task, not yet via public REST.