Only message events are supported in this first pass:
message.created,
message.updated, and message.deleted.Authentication
Send your project key in thex-modem-public-key header.
- Open your organization in the Modem dashboard.
- Go to Organization → Projects.
- Open the project you want to ingest into.
- In Public Keys, create a key or copy an existing active key.
Despite the name, treat this key like a server-side bearer credential. Do not
ship it in browser code or mobile apps.
Endpoint
x-modem-public-key header:
Event Envelope
Every request sends a single event.| Field | Required | Notes |
|---|---|---|
event_type | Yes | message.created, message.updated, or message.deleted |
created_at | Yes | ISO 8601 timestamp for when your integration emitted the event |
received_at | No | ISO 8601 timestamp for when you received it from the upstream source |
source_nonce | No | Upstream delivery ID for deduplication — see Deduplication before using |
client | Yes | Producer metadata for the integration sending the event |
body | Yes | Event payload |
client
client.name identifies the producer that submitted the event to Modem.
- Use a lowercase package-style identifier such as
@your-company/teams-sync. @modem/*is reserved for Modem-managed integrations.client.nameis descriptive producer metadata. It is not a trusted provenance boundary.
client.version must use semantic versioning, for example 1.0.0.
Message Payload
Formessage.created and message.updated, body looks like this:
Required fields
| Field | Notes |
|---|---|
source_name | Stable lowercase slug for the message source |
source_message_id | Stable upstream message ID |
source_message_type | Stable upstream message classification, usually message |
created_at | Message creation timestamp from the source |
channel | Source workspace and channel info |
content, title, or attachments | At least one must be present |
Important rules
channel.source_namemust exactly matchdata.source_name.source_nameshould stay stable within your organization. Modem uses it as part of deterministic IDs.- Custom sources use plain slugs directly in
source_name. Do not prefix them withcustom_. - Built-in values like
slack,discord, andgithubremain reserved for those sources.
Threading and replies
Use these optional pairs when you have them:source_thread_idandsource_thread_typesource_reply_to_idandsource_reply_to_type
Custom Sources
Custom message sources are supported for message events.- Good
source_namevalues:microsoft-teams,intercom,zendesk-chat - Better when one upstream app needs multiple distinct feeds:
teams-support,teams-sales - Avoid renaming a source later by changing the slug. That creates a new source identity inside Modem.
source_name is the source identity. There is no separate source_key.
Examples
message.created
message.updated
message.deleted
Responses
Successful requests return200 OK.
| Status | When it happens |
|---|---|
400 | Invalid JSON, invalid schema, or no extractable text content |
401 | Missing or invalid project key |
405 | Method other than POST |
500 | Event could not be queued |
Processing Behavior
message.createdstores the message, runs attachment OCR when needed, then sends the message into grouping and topic processing.message.updatedupdates the stored message and can OCR newly added image attachments. It does not re-run grouping or topic generation for existing conversations.message.deletedmarks the stored message deleted and clears message content.
Deduplication
source_nonce exists for one specific scenario: your upstream source gives you a delivery ID that is unique to each delivery attempt, and that source may retry or redeliver the same payload. Setting source_nonce to that delivery ID lets Modem drop the duplicate.
Good example — GitHub webhooks: GitHub assigns every webhook delivery an X-GitHub-Delivery header. GitHub may redeliver the same webhook multiple times within seconds. Using that delivery ID as source_nonce is correct because it is scoped to the delivery, not to the underlying resource.
source_nonce from characteristics of the message itself (such as source_message_id, channel ID, or a combination of fields), you will block all future events that touch that message. A message.updated event with the same nonce as the original message.created would be silently dropped as a duplicate — the update never gets applied.
How it works
Whensource_nonce is present, Modem generates a deterministic event ID from:
source_nonce, each request gets a unique event ID and Modem treats it as a new delivery — which is the correct behavior for most integrations.
Current Limits
- Public ingest is message-only in this version.
- Pull request and issue event families remain built-in-only.
- There is no public OpenAPI endpoint yet.
- Internal vs external producer provenance is not enforced by auth yet.
Related
Integrations
See Modem’s built-in integrations and how they capture data.
Security & Privacy
Review the current security model for data sent to Modem.