Skip to main content
A message is one post, comment, or reply inside a source container: a chat message in a channel, a customer’s reply on a ticket, a comment on an issue. Messages are what Modem reads; source containers tell it how to read them. Three event types manage messages: message.created, message.updated, and message.deleted. All use the standard envelope.

The source container reference

Every message carries a reference to the source container it belongs to:
Modem resolves the reference using the message’s source_name plus these two fields, so all three must match the source container’s identity exactly. The reference is what connects a message to processing. Messages are queued against their source container and drained in order, with the source container’s kind, title, description, and lifecycle state as context. Without a matching source container:
  • A message with a reference to a source container Modem has not seen yet is stored and waits. It is processed once the source container’s source_container.created event arrives. A dangling reference is fine; it just delays processing.
  • A message with no reference at all is stored but not processed.
The source_container reference will soon be required. Messages without one are not processed today, and will be rejected with a 400 once the requirement is enforced.
A message’s source container is set once. Re-sending the message with a different reference does not move it.

Message payload

For message.created and message.updated, body.data looks like this:

Required fields

channel

channel describes the broader place a message came from and is used for filtering and display. It is coarser than the source container. channel.source_name must equal data.source_name.

author

author is optional, but a message without one contributes nothing to the people and companies Modem tracks. Send it whenever you can.
Bot-authored content is filtered during extraction. If a bot relays human-written text (an alert feed that quotes a customer, a form that posts submissions), leave is_bot false or Modem will discard the content.

Optional fields

Threading and replies

Source containers scope the conversation; threads and replies describe structure inside it.
  • source_thread_id and source_thread_type identify the thread a message belongs to (a reply thread in a chat channel, for example). Modem treats distinct threads inside one source container as distinct concerns by default.
  • source_reply_to_id and source_reply_to_type identify the specific message this one replies to.
Each pair must be sent together or omitted together. For kinds where the source container is the thread (support_thread, issue, email_thread), you can omit both.

Examples

message.created

The first message in a chat channel source container:
A comment on an issue source container:

message.updated

Same body as message.created, with the new content and an edited_at:

message.deleted

Processing behavior

  • message.created stores the message, runs OCR on image attachments, and queues the message for processing in its source container. Modem drains each source container serially: messages are read in order, in windows, with earlier concerns from the same source container available so restatements are merged rather than duplicated.
  • message.updated updates the stored content and OCRs newly added images. It does not re-run extraction for content that was already processed.
  • message.deleted marks the message deleted and clears its content.
Source container lifecycle events feed the same pipeline. When a source container’s state changes, that transition is recorded and used as evidence when Modem assesses the lifecycle of the topics the source container contributes to. See Why state matters.

Deduplication

Most integrations should not set source_nonce. Only use it when your upstream provider has at-least-once delivery and may send you the same event more than once. If you construct events yourself, leave it empty.
source_nonce exists for one scenario: your upstream source gives you a delivery ID that is unique per delivery attempt, and that source may redeliver the same payload. Setting source_nonce to that delivery ID lets Modem drop the duplicate. Good example: a webhook provider assigns every delivery a unique ID header and may redeliver the same webhook within seconds. Using that delivery ID as source_nonce is correct because it is scoped to the delivery, not to the underlying resource.
Bad example: building source_nonce from properties of the message (its ID, channel, or a combination). A message.updated with the same nonce as the original message.created would be silently dropped as a duplicate, and the update never applies.

How it works

When source_nonce is present, Modem generates a deterministic event ID from:
A second request with the same nonce produces the same event ID and is treated as a duplicate delivery. The nonce must identify the delivery, not the resource. Without source_nonce, each request gets a unique event ID, which is the correct behavior for most integrations. The same applies to source_container.* events.

Source Containers

Source container kinds, identity, lifecycle state, and the source container events.

Ingest API overview

Authentication, the event envelope, and the end-to-end protocol.