Webhook Events
Every event Meldoc sends shares the same envelope — only the event name and data field differ.
Payload envelope
{
"delivery_id": "f1b4e12c-8acb-41c1-8e1a-1dfe8e7e2e11",
"event": "doc.published",
"workspace_id": "8e53...",
"project_id": "4a2c...",
"project_alias": "api",
"data": {},
"triggered_at": "2026-04-18T10:00:00Z"
}
project_id and project_alias are omitted for workspace-scope events. Glossary events are always workspace-scope; member events are workspace-scope only when the change isn’t tied to a specific project (see Member events below).
Filtering events
When creating a webhook you can subscribe to:
- All events — receive every event type.
- Category wildcard — for example
doc.*matches all document events. - Exact type — for example
doc.publishedonly.
Document events
| Event | Fires when | Key data fields |
|---|---|---|
docs.changed |
Any document is created, updated, or deleted | { created: [], updated: [], deleted: [] } — arrays of { id, alias, title, updated_at } |
doc.published |
Document transitions from draft to published | { doc } |
doc.unpublished |
Document transitions from published to draft | { doc } |
doc.moved |
Document’s parent changes | { doc, from_parent_alias, to_parent_alias } |
doc.project_changed |
Document (and its subtree) moves to another project in the same workspace | { doc, from_project_id, from_project_alias, to_project_id, to_project_alias, from_parent_alias, to_parent_alias, subtree_count } |
doc.exposure_changed |
Document exposure level changes | { doc, from, to } |
doc.archived |
Document enters the archive (see Archiving Documents) | { doc, replaced_by, cascaded } — fires once per affected document; cascaded is true for descendants archived with a subtree |
doc.unarchived |
Document is restored from the archive | { doc, cascaded } — never cascaded; restoring affects one document |
Note:
docs.changedis debounced — rapid edits are batched into a single delivery roughly every 10 seconds. Lifecycle events (doc.published,doc.unpublished,doc.moved,doc.project_changed,doc.exposure_changed,doc.archived,doc.unarchived) fire immediately and are additive todocs.changed. Subscribe to only the lifecycle events if you don’t need to track every edit.doc.project_changedfires in the context of the destination project, so the envelope’sproject_idandproject_aliasreflect the target.
Deleting a document reports its whole subtree in docs.changed — every descendant appears in the deleted array, not just the document that was acted on.
Project events
| Event | Fires when | Key data fields |
|---|---|---|
project.created |
New project created | { project } |
project.updated |
Name, alias, or settings changed | { project, changed_fields: [...] } |
project.deleted |
Project deleted | { project } |
Member events
Member events fire for both workspace-level and project-level changes. When project_id is absent from the envelope, the change is workspace-level.
| Event | Fires when | Key data fields |
|---|---|---|
member.added |
User added to workspace or project | { member } |
member.removed |
User removed | { member } |
member.role_changed |
Role updated | { member, from_role, to_role } |
Glossary events
All glossary events are workspace-scope — project_id is always omitted from the envelope.
| Event | Fires when | Key data fields |
|---|---|---|
glossary.term_added |
Term created | { term } |
glossary.term_updated |
Definition, aliases, or settings changed | { term } |
glossary.term_deleted |
Term removed | { term } (snapshot at delete time) |
glossary.imported |
Bulk import completed | { created, updated, total } |
Note: Bulk import fires a single
glossary.importedevent rather than one per term. Fetch the current term list from the Glossary page if you need the full updated state.
What’s next?
Verifying Webhook Signatures — Verify that events come from Meldoc.
Delivery and Retries — Retry behavior and the delivery log.