Events and webhooks
An event is a thing that happened to a record. Events are what wires fire on, what notifications are raised from, and what a webhook can carry out to another system.
The event vocabulary
Events are named module.thing.verb. The module owns the name, so the source is
always legible:
inventory.part.created,inventory.stock.changed,inventory.stock.lowpurchases.order.arrived,purchases.order_item.receiveddigifab.print.completed,digifab.print.failure_suspected
Each module declares the events it announces in its manifest, and only declared events show up as wire triggers. There is no single generated catalog page yet, so the source of truth is the module itself: read a module's declared events, or list them from the workspace, rather than working from a copy that can drift.
Every event carries a payload that includes the workspace id and, by convention,
the id of the record it concerns (partId, taskId, and so on). When an event is
emitted it fans out two ways at once: to any module code subscribed to it, and to
the user-configured wires that match it.
Outbound: send events to a URL
The webhook notification channel POSTs events to a URL you own. You set it up per workspace as a notification channel (alongside email, Slack, and Discord); see Notifications. The config is a URL and an optional set of headers, so you can carry your own auth token to the receiver.
The body is a stable JSON envelope:
{
"notification_id": "…",
"event_type": "purchases.order.arrived",
"message": "Order from Acme arrived",
"link_url": "/purchases/orders/…",
"priority": "normal",
"org_id": "…",
"user_id": "…",
"occurred_at": "2026-07-11T18:20:00.000Z"
}
A webhook URL that resolves to an internal or loopback address is refused, so a misconfigured hook can not be turned into a probe of the host's own network. On a self-hosted install you can allow a LAN target with a config flag; see Self-hosting.
Slack and Discord are their own channels rather than something you build on top of the raw webhook: point the channel at an incoming webhook URL from that service and Cobblr formats the message for it.
Inbound: let another system ping a workspace
A connector can expose a per-workspace inbound URL that carries a token instead of a login:
POST /api/v1/integrations/<connector>/<token>/webhook
The token resolves to a workspace, so the URL needs no slug and no session. The
connector's handler verifies the request (an HMAC signature against a shared
secret it stores, for instance), then translates the payload into a Cobblr event
that wires and notifications react to. Each hit is audited and its counters
bumped, and a token can be revoked, which starts returning 410 Gone. Set these
up from Integrations; the available
connectors are in the integration catalog.
There is also an instance-level receiver at /api/v1/hooks/<id> for provider
webhooks that concern the whole install rather than one workspace (a billing
provider's callback, say). Open Cobblr registers no handlers there, so every id
returns 404 until an overlay or module adds one.
Wiring inbound to outbound
Because an inbound webhook becomes an ordinary event, a wire can react to it, and that wire's action can raise a notification that an outbound webhook carries onward. The three pieces are separate on purpose, and they compose end to end without any of them knowing about the others.