Webhook events

Webhook event: message.sent

The payload Cooledge sends when someone on the team sends a text or email from Messages, including a draft.

Updated 11/10/2026

message.sent

Sent when someone on the team sends a text or email from Messages, on the portal or in the app. That covers a reply from the composer, a Send later message when it goes, a message held by undo send once it really goes and a draft a person sends. Delivered to your webhook subscriptions. Deliveries are at-least-once, so dedupe on X-Cooledge-Event-Id.

Quotes and invoices sent from their own screens have their own events (quote.sent and invoice.sent) and do not fire it.

It is best effort. Cooledge records the event as the message goes. It never holds up the send to do it. In rare cases, such as a slow moment in Cooledge's database, the event is not recorded and is lost, with no retry. Once it is recorded, delivery to your endpoint is retried like any other event. If you need a complete record of what was sent, read the conversation with GET /v1/conversations/{id}/messages.

Subscribing to this event through the API needs the messages:read scope. See the events index.

Envelope

Every event shares the flat envelope: event_type, version ("v1"), event_id, business_id, occurred_at, plus the fields below. See the events overview for the envelope detail and webhooks for signature verification and delivery semantics.

Fields

FieldTypeNotes
messageobjectThe message.
message.idstringUUID of the message. The same id the messages endpoint returns.
message.directionstringAlways "out" for this event.
message.channelstring"sms" or "email".
message.subjectstring or nullThe email subject. Null for a text.
message.textstring or nullThe plain text body.
message.htmlstring or nullThe email's HTML. Null for a text.
message.fromstring or nullThe business number or address it was sent from.
message.tostring or nullThe phone number or email address it was sent to.
message.ccarray of stringsEmail CC addresses. Empty for a text.
message.atstringISO 8601 timestamp for when the message was sent.
message.attachmentsarrayThe files on the message, each { filename, content_type, size } with content_type and size (in bytes) possibly null. Inline email images are left out. Empty when there are none.
conversationobject or nullThe conversation the message is in: { id, state, is_unmatched }. state is one of open, snoozed, archived, trashed or spam. is_unmatched is true when the recipient is not matched to a customer.
customerobject or nullThe matched customer. Null when the recipient is an unknown sender.
customer.idstringUUID of the customer.
customer.namestring or nullThe customer's display name.
customer.emailstring or nullThe customer's saved email, which may differ from the address the message went to.
customer.phonestring or nullThe customer's saved phone, which may differ from the number the message went to.
contactobject or nullThe customer's contact the message went to, when it went to one. Null otherwise.
contact.idstringUUID of the contact.
contact.namestring or nullThe contact's first and last name.
sender_addressstring or nullAn unknown sender's phone number or email address. Null when the conversation is matched to a customer.
sent_byobjectThe team member who sent it. For a Send later message, the person who scheduled it.
sent_by.idstringUUID of the team member.
sent_by.namestring or nullThe team member's name.
draftobject or nullSet when the message was a draft. Null for any other message.
draft.idstringThe id the draft had when it was sent, as GET /v1/drafts/{id} reads it.
draft.drafted_by.kindstringuser, api_key or oauth.
draft.drafted_by.namestring or nullThe person, API key or connected app that wrote it, as named when the draft was saved.

A draft a person sent and then undid comes back with a new id, so if that new draft is sent later, draft.id is the new id, not the one your agent created. See Following a draft.

Example

{
  "version": "v1",
  "event_type": "message.sent",
  "event_id": "f2c9a6d1-8e43-5b7a-9d10-3e6b8c4a2f57",
  "business_id": "a7f3c9d1-2e84-4b6f-8c01-5d9e2a1f7b30",
  "occurred_at": "2026-10-11T03:31:08.442Z",
  "message": {
    "id": "7d1e5b93-2c48-4a6f-b0e7-9f3a2d8c6b14",
    "direction": "out",
    "channel": "sms",
    "subject": null,
    "text": "Hi Priya, Thursday works. We can be there between 8 and 10 am. Does that suit?",
    "html": null,
    "from": "+61480012345",
    "to": "+61498765432",
    "cc": [],
    "at": "2026-10-11T03:31:07.915Z",
    "attachments": []
  },
  "conversation": {
    "id": "8f3a1c52-6d47-4b9e-a210-5c7e9d2b4f18",
    "state": "open",
    "is_unmatched": false
  },
  "customer": {
    "id": "c4a9e1b2-7d3f-4a8c-9e2b-1f6a0d5c3b7e",
    "name": "Priya Nair",
    "email": "priya.nair@example.com",
    "phone": "+61498765432"
  },
  "contact": null,
  "sender_address": null,
  "sent_by": {
    "id": "5b2e7d90-1c34-4f8a-9e61-0d3c7a2b5e49",
    "name": "Jess Taylor"
  },
  "draft": {
    "id": "e71c4b28-3f95-4d0a-8c6e-2a9b5d7f1e30",
    "drafted_by": { "kind": "api_key", "name": "Office assistant" }
  }
}

See webhooks for signature verification and delivery semantics. See all events for the full list.

Need a hand with an integration? Contact support