agentlyleads docs
Webhooks

Events and payloads

Every request body has the same envelope: id, type, createdAt, workspaceId and data. This page describes data for each event.

Records inside data use the same shape the v1 API returns for them, so code that already reads GET /api/v1/contacts can read data.contact. Dates are ISO 8601 strings and money is a number.

EventFires whendata
contact.createdA contact is added: in the app, by import, the API, a HubSpot or Zoho sync, or MCPcontact
contact.updatedA contact's fields are savedcontact, changedFields
lead.createdA lead is added, including from forms, bookings and POST /api/v1/leadslead
deal.createdA deal is addeddeal
deal.stage_changedA deal moves stage: an edit, a bulk move, or a drag on the pipeline boarddeal, previousStage
task.createdA task, call, email reminder or meeting is addedtask
booking.createdSomeone books time through a booking linkbooking
booking.rescheduledA booked meeting moves to a new timebooking, previousStartAt, previousEndAt
booking.cancelledA booked meeting is cancelled by the invitee or the hostbooking
chat.startedA website visitor sends the first message of a new chatconversation, firstMessage
email.receivedAn inbound email arrives in a connected mailboxemail
form.submittedSomeone submits an embedded lead capture formform, lead, leadCreated, pageUrl, fields
webhook.testYou press Send test event, or call the test endpointmessage, endpointId

contact.created and contact.updated

data.contact is the Contact object. contact.updated adds changedFields, the fields that were written in that save (a field written with its current value is still listed).

{
  "id": "evt_0c6e2f1a9b8d7c5e4f3a2b1c",
  "type": "contact.updated",
  "createdAt": "2026-09-27T14:00:00.000Z",
  "workspaceId": "cmq1wksp0alk34567890",
  "data": {
    "contact": {
      "id": "cmq2cont0alk34567891",
      "name": "Ann Lee",
      "firstName": "Ann",
      "lastName": "Lee",
      "title": "CTO",
      "email": "ann@example.com",
      "phone": null,
      "status": "NEW",
      "lifecycleStage": null,
      "source": null,
      "category": null,
      "buyerType": null,
      "website": null,
      "city": null,
      "state": null,
      "country": null,
      "doNotContact": false,
      "emailOptOut": false,
      "emailStatus": null,
      "engagementScore": null,
      "lastContactedAt": null,
      "lastActivityAt": null,
      "externalId": null,
      "company": { "name": "Acme", "externalId": null },
      "customFields": null,
      "createdAt": "2026-09-20T10:00:00.000Z",
      "updatedAt": "2026-09-27T14:00:00.000Z"
    },
    "changedFields": ["title"]
  }
}

lead.created

data.lead is the Lead object.

{
  "lead": {
    "id": "cmq2lead0alk34567891",
    "name": "Eve Park",
    "title": null,
    "company": "Northwind",
    "email": "eve@example.com",
    "phone": null,
    "source": "Web form: Contact us",
    "status": "NEW",
    "estimatedInterest": "MEDIUM",
    "owner": null,
    "interestedOffering": null,
    "customFields": null,
    "createdAt": "2026-09-27T14:00:00.000Z",
    "updatedAt": "2026-09-27T14:00:00.000Z"
  }
}

deal.created and deal.stage_changed

data.deal is the Deal object with two more fields: pipelineId and pipelineStage ({ id, name }), the stage as it appears on your pipeline board. stage is the fixed stage (NEW, QUALIFIED, PROPOSAL, NEGOTIATION, WON, LOST).

deal.stage_changed adds previousStage. When the move was a drag on the board it also adds previousStageId, because two board stages can share one fixed stage.

{
  "deal": {
    "id": "cmq2deal0alk34567891",
    "name": "Northwind renewal",
    "value": 12500,
    "stage": "WON",
    "expectedCloseDate": null,
    "notes": null,
    "externalId": null,
    "contact": { "name": "Ann Lee", "email": "ann@example.com", "externalId": null },
    "company": { "name": "Northwind", "externalId": null },
    "offering": null,
    "campaign": null,
    "customFields": null,
    "createdAt": "2026-09-01T09:00:00.000Z",
    "updatedAt": "2026-09-27T14:00:00.000Z",
    "pipelineId": "cmq2pipe0alk34567891",
    "pipelineStage": { "id": "cmq2stag0alk34567891", "name": "Won" }
  },
  "previousStage": "NEGOTIATION",
  "previousStageId": "cmq2stag0alk34567890"
}

task.created

data.task is the Task object. Meetings booked through a booking link are tasks with type: "MEETING".

booking.created, booking.rescheduled, booking.cancelled

There is no v1 bookings endpoint, so data.booking has its own shape:

FieldTypeNotes
idstringThe booking ID
statusstringCONFIRMED or CANCELLED
startAt, endAtstringThe meeting's start and end
hostTimezone, inviteeTimezonestringIANA time zone names
inviteeobjectname, email, phone, notes
guestsstring[]Extra attendee emails
answersarray{ label, value } for each booking question, as asked
meetingTypeobjectid, title, slug, durationMin
hostobjectid, name, email of the person booked
joinUrlstring or nullThe video link. Often null on booking.created, because the link is made a moment later when the calendar event is written
contactId, leadId, taskIdstring or nullThe CRM records the booking is attached to
cancelledBy, cancelReason, cancelledAtSet on booking.cancelled. cancelledBy is invitee or host
createdAtstringWhen it was booked

booking.rescheduled adds previousStartAt and previousEndAt.

{
  "booking": {
    "id": "cmq2book0alk34567891",
    "status": "CONFIRMED",
    "startAt": "2026-09-28T09:30:00.000Z",
    "endAt": "2026-09-28T10:00:00.000Z",
    "hostTimezone": "America/Toronto",
    "inviteeTimezone": "Europe/London",
    "invitee": { "name": "Dana Ruiz", "email": "dana@example.com", "phone": null, "notes": null },
    "guests": [],
    "answers": [{ "label": "What would you like to cover?", "value": "Pricing" }],
    "meetingType": { "id": "cmq2type0alk34567891", "title": "Intro call", "slug": "intro", "durationMin": 30 },
    "host": { "id": "cmq2user0alk34567891", "name": "Qasim", "email": "qasim@example.com" },
    "joinUrl": "https://meet.google.com/abc-defg-hij",
    "contactId": null,
    "leadId": "cmq2lead0alk34567891",
    "taskId": "cmq2task0alk34567891",
    "cancelledBy": null,
    "cancelReason": null,
    "cancelledAt": null,
    "createdAt": "2026-09-27T14:00:00.000Z"
  },
  "previousStartAt": "2026-09-21T09:00:00.000Z",
  "previousEndAt": "2026-09-21T09:30:00.000Z"
}

chat.started

Fires on the visitor's first message, not when they open the chat panel (people open it and leave).

{
  "conversation": {
    "id": "cmq2chat0alk34567891",
    "channel": "WEB",
    "site": { "id": "cmq2site0alk34567891", "name": "Main site" },
    "handler": "WAITING",
    "visitor": { "name": null, "email": null, "phone": null, "locale": "en-GB", "timezone": "Europe/London" },
    "pageUrl": "https://www.example.com/pricing",
    "referrer": null,
    "contactId": null,
    "leadId": null,
    "createdAt": "2026-09-27T13:59:40.000Z"
  },
  "firstMessage": { "id": "cmq2cmsg0alk34567891", "body": "Do you ship to Canada?", "createdAt": "2026-09-27T14:00:00.000Z" }
}

email.received

Fires for new inbound mail in a connected mailbox. Newsletters and other bulk mail, bounce reports, and mail older than the mailbox connection (a first sync importing history) do not fire it. The body is not included; fetch it with GET /api/v1/emails/{id}.

{
  "email": {
    "id": "cmq2mail0alk34567891",
    "status": "SENT",
    "direction": "INBOUND",
    "subject": "Re: Quote for 200 units",
    "toEmail": "sales@example.com",
    "fromEmail": "ann@example.com",
    "ccEmails": [],
    "contactId": "cmq2cont0alk34567891",
    "leadId": null,
    "campaignId": null,
    "threadId": "18c2f0a1b2c3d4e5",
    "emailAccountId": "cmq2acct0alk34567891",
    "replyClass": null,
    "sentAt": "2026-09-27T13:59:58.000Z",
    "createdAt": "2026-09-27T14:00:00.000Z",
    "reviewUrl": "https://agentlyleads.com/emails/cmq2mail0alk34567891"
  }
}

form.submitted

Fires for every accepted submission of an embedded form. Submissions caught by the spam checks do not fire it. When the email matches an existing lead, that lead is updated and leadCreated is false.

{
  "form": { "id": "cmq2form0alk34567891", "name": "Contact us" },
  "lead": { "id": "cmq2lead0alk34567891", "name": "Eve Park", "email": "eve@example.com", "...": "..." },
  "leadCreated": true,
  "pageUrl": "https://www.example.com/contact",
  "fields": { "name": "Eve Park", "email": "eve@example.com", "message": "Do you offer volume pricing?" }
}

fields holds the form's configured fields as submitted (trimmed, empty ones as null).

webhook.test

{
  "message": "This is a test event from AgentlyLeads. If you can read it, your endpoint works.",
  "endpointId": "cmq9hook0alk34567890"
}

On this page