agentlyleads docs
Calendar API

The Calendar entry object

Every item in the entries array returned by GET /api/v1/calendar shares the same shape. Which fields carry a value depends on kind.

FieldTypeNotes
idstringA typed, stable id — see "The id format" below. Also usable as an ICS-style UID.
kindstringOne of task, deal-close, campaign, followup, birthday
datestring (YYYY-MM-DD)The calendar day this entry belongs to
startAtstring (ISO 8601) | nullFull instant, only for a timed entry — null for all-day entries
endAtstring (ISO 8601) | nullSame rule as startAt
allDaybooleantrue for every kind except a timed task
titlestringDisplay title
hrefstringIn-app agentlyleads URL — a deep link back into the CRM
metastring | nullOne-line human-readable context, or null
donebooleantask entries only — whether the task is complete
taskIdstringtask entries only — the raw task ID
seriesIdstring | nulltask entries only — the repeating series this occurrence belongs to

The id format

id is built from the kind and the underlying record, and is stable across requests:

Kindid shapeExample
tasktask:<taskId>"task:cmq7task0alk34567890"
deal-closedeal:<dealId>"deal:cmq3deal0alk34567890"
campaigncampaign:<campaignId>"campaign:cmq9camp0alk34567890"
followupfollowup:<emailId>"followup:cmq2mail0alk34567890"
birthdaybirthday:<contactId>:<year>"birthday:cmq5cont0alk34567890:2026"

A birthday recurs yearly, so its id includes the occurrence's year — the same contact's birthday next year is a different entry with a different id.

date vs. startAt/endAt

date is always set — it is which calendar day the entry belongs to, and is what a month grid or day list buckets on. startAt/endAt are only set when the entry is a real moment in time (a timed task/appointment); never read one against the other's accessor. All entries other than a timed task are allDay: true with startAt/endAt both null — a deal close, a scheduled campaign send, a follow-up, and a birthday are always all-day.

Kind by kind

task

A Task row whose dueDate falls in the range — the same underlying record as GET /api/v1/tasks, reshaped into the merged calendar format. A timed task (dueDate carries a clock time) is a real appointment: allDay is false and startAt/endAt are set. An all-day task (a plain to-do, or one with no endAt beyond its due date) has allDay: true and null startAt/endAt.

Only task entries carry done, taskId, and seriesId. href is the generic /tasks list, not a per-task deep link. meta is the linked contact's or deal's name, whichever is set (null if neither).

{
  "id": "task:cmq7task0alk34567890",
  "kind": "task",
  "date": "2026-09-10",
  "startAt": "2026-09-10T18:00:00.000Z",
  "endAt": "2026-09-10T18:30:00.000Z",
  "allDay": false,
  "title": "Discovery call with Acme",
  "href": "/tasks",
  "meta": "Jane Buyer",
  "done": false,
  "taskId": "cmq7task0alk34567890",
  "seriesId": null
}

deal-close

An open deal (stage not WON/LOST) whose expectedCloseDate falls in the range. Always all-day. title is "Close: <deal name>"; meta is "$<value> · <stage>"; href links to the deal.

{
  "id": "deal:cmq3deal0alk34567890",
  "kind": "deal-close",
  "date": "2026-09-12",
  "startAt": null,
  "endAt": null,
  "allDay": true,
  "title": "Close: Acme Corp — Annual Contract",
  "href": "/deals/cmq3deal0alk34567890",
  "meta": "$12,000 · QUALIFIED"
}

campaign

A SCHEDULED campaign whose scheduledAt falls in the range. Always all-day — a send has a scheduled instant, but it isn't something anyone attends, so it doesn't carry startAt/endAt. title is "Campaign: <name>"; meta is always "scheduled send"; href links to the campaign.

{
  "id": "campaign:cmq9camp0alk34567890",
  "kind": "campaign",
  "date": "2026-09-09",
  "startAt": null,
  "endAt": null,
  "allDay": true,
  "title": "Campaign: September newsletter",
  "href": "/campaigns/cmq9camp0alk34567890",
  "meta": "scheduled send"
}

followup

An email thread that's gone quiet and is due for a follow-up. Always dated today (in the workspace's timezone) — it only appears if today falls inside the requested range, regardless of from/to. title is "Follow up: <contact name>"; meta is "<N>d quiet · <subject>"; href links to the email thread.

{
  "id": "followup:cmq2mail0alk34567890",
  "kind": "followup",
  "date": "2026-09-08",
  "startAt": null,
  "endAt": null,
  "allDay": true,
  "title": "Follow up: Jane Buyer",
  "href": "/emails/cmq2mail0alk34567890",
  "meta": "3d quiet · Re: pricing question"
}

birthday

A contact with a birthday set, surfaced on every yearly occurrence that falls in the range — a contact whose birthday you're tracking across a range spanning two years appears twice, once per year. Always all-day, meta is always null. href links to the contact.

{
  "id": "birthday:cmq5cont0alk34567890:2026",
  "kind": "birthday",
  "date": "2026-09-11",
  "startAt": null,
  "endAt": null,
  "allDay": true,
  "title": "Jane Buyer's birthday",
  "href": "/contacts/cmq5cont0alk34567890",
  "meta": null
}

external is never returned

Internally, the CRM calendar has a sixth kind, external — a user's connected Google/Outlook calendar, read in as busy time. GET /api/v1/calendar never returns it: those events are only fetched when the caller is a specific signed-in user, and a workspace API key has no single "current user" to attribute them to. Returning them here would leak whichever user's personal calendar to every caller holding the key, so they're deliberately left out — this is not a gap to be filled later, it's the contract.

On this page