agentlyleads docs
Tasks API

POST /api/v1/tasks

Create or update up to 1,000 tasks in a single request. Each row is matched and upserted by externalId; rows with no match are created.

A task is also how calendar appointments work in agentlyleads: send a dueDate with a clock time (and use type: "MEETING") and it shows up on the CRM calendar as a timed appointment. Send a bare date, or no dueDate, and it's a plain all-day to-do.

Request — book a one-off appointment

The natural showcase for this endpoint: a booking-site integration creating a calendar appointment linked to a contact it already pushed via /api/v1/contacts.

POST /api/v1/tasks HTTP/1.1
Host: agentlyleads.com
Authorization: Bearer alk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

{
  "tasks": [
    {
      "title": "Discovery call with Acme",
      "type": "MEETING",
      "dueDate": "2026-09-10T14:00:00",
      "endAt": "2026-09-10T14:30:00",
      "contactExternalId": "hs_contact_123",
      "externalId": "booking_9F3K2"
    }
  ]
}

Note the dueDate/endAt shapes: no Z and no offset. That's what makes agentlyleads read 14:00:00 as 2pm in the workspace's timezone, not 2pm UTC. See The Task object for the full explanation of all three accepted shapes.

Request — a plain all-day to-do

POST /api/v1/tasks HTTP/1.1
Host: agentlyleads.com
Authorization: Bearer alk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

{
  "tasks": [
    {
      "title": "Send updated pricing sheet",
      "dueDate": "2026-09-10",
      "priority": "HIGH"
    }
  ]
}

A bare "YYYY-MM-DD" dueDate (no clock time) makes this an all-day entry — a to-do, not an appointment. It won't show up with a clock time on the calendar.

The tasks array must contain at least 1 item and at most 1,000.

Response — 200 (all rows succeeded)

When every row in the batch succeeds, the response status is 200:

{
  "created": 1,
  "updated": 1,
  "errors": 0,
  "results": [
    { "index": 0, "status": "created", "id": "cmq7task0alk34567890" },
    { "index": 1, "status": "updated", "id": "cmq8task1alk34567891" }
  ]
}

Each entry in results corresponds to the task at that position in your request array (zero-indexed).

Response — 207 Multi-Status (partial success)

When at least one row fails validation, the response status is 207. Failed rows appear in results with "status": "error":

{
  "created": 1,
  "updated": 0,
  "errors": 1,
  "results": [
    { "index": 0, "status": "created", "id": "cmq7task0alk34567890" },
    {
      "index": 1,
      "status": "error",
      "error": "contactExternalId: no contact found for \"NOPE\"."
    }
  ]
}

A row that fails validation does not abort the batch. Valid rows are written; failed rows are skipped and reported per-index in results.

Merge on update

When a row matches an existing task (by externalId), only the fields present in your request body are updated. Fields you omit retain their current values — this is a merge update, not a full replace.

For example, re-sending { "externalId": "booking_9F3K2", "done": true } marks the appointment done without touching its title, dueDate, or links. Retrying the same booking with the same externalId and a new dueDate reschedules it in place, rather than creating a duplicate appointment.

Linking a contact, lead, or deal

A task can be linked to a contact, lead, and/or deal either by their agentlyleads record ID (contactId/leadId/dealId) or by an external system's ID (contactExternalId/leadExternalId/dealExternalId), which the server resolves against that record's own externalId:

{
  "tasks": [
    {
      "title": "Follow-up call after demo",
      "type": "CALL",
      "dueDate": "2026-09-12T10:00:00",
      "contactExternalId": "hs_contact_123",
      "dealExternalId": "hs_deal_789"
    }
  ]
}

If an *ExternalId is supplied but no matching record is found in the workspace, that row fails — it does not create the task unlinked.

Recommended push order: the contact, lead, or deal a task links to must already exist in the workspace before you push the task. Push contacts/leads/deals first, then tasks referencing them by external ID.

Assigning to a rep

Set assigneeId (agentlyleads user ID) or assigneeEmail (their workspace login email) to assign the task/appointment to a sales rep or staff member. Omit both to leave it unassigned:

{
  "tasks": [
    {
      "title": "Onboarding call",
      "type": "MEETING",
      "dueDate": "2026-09-15T09:00:00",
      "assigneeEmail": "rep@yourcompany.com"
    }
  ]
}

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.

Quickstart

export AL_KEY="alk_live_xxx"

# Book an appointment — this is what a booking-site webhook typically calls
curl -s -X POST https://agentlyleads.com/api/v1/tasks \
  -H "Authorization: Bearer $AL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tasks": [
      {
        "title": "Discovery call with Acme",
        "type": "MEETING",
        "dueDate": "2026-09-10T14:00:00",
        "endAt": "2026-09-10T14:30:00",
        "contactExternalId": "hs_contact_123",
        "externalId": "booking_9F3K2"
      }
    ]
  }'

A typical integration is a booking site calling this endpoint the moment a customer books a slot, using the booking's own ID as externalId. If the customer reschedules, POST the same externalId again with a new dueDate/endAt to update the existing appointment in place, instead of creating a second one.

On this page