Skip to main content

Webhooks,
signed and on time.

Murmurly posts each update to your URL as it happens. It never pulls anything back.

“Connect an endpoint, check my signature, answer 2xx. That’s it.”

Murble in a cap with the Murmurly logo, next to a mug with curly braces on it
01

Connect an endpoint

Admins · Route signals to Webhook
  1. Open Webhooks

    Settings → Integrations & APIs → Webhooks.

  2. Add your URL

    Give it a name and an https URL, then Connect endpoint. Private network addresses are refused.

  3. Copy the secret

    It is shown once. Store it on your server, never in code.

  4. Route signals

    Under Signal Routing, switch on what this endpoint should receive.

02

Verify every delivery

Same scheme as Stripe

X-Murmurly-Signature: t=<unix>,v1=<hex>

HMAC-SHA256 of <t>.<raw body> with your secret. Accept if any v1 matches, and reject anything older than 300 seconds.

import crypto from 'node:crypto'

// Use the raw request body, exactly as received.
export function verify(rawBody, header, secret) {
  const parts = header.split(',')
  const t = parts.find((p) => p.startsWith('t='))?.slice(2)
  const sigs = parts.filter((p) => p.startsWith('v1=')).map((p) => p.slice(3))
  if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
  return sigs.some((s) => s.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected)))
}
03

What arrives

POST https://hooks.example.com/murmurly
X-Murmurly-Signal: task.assigned
X-Murmurly-Delivery: <delivery id>
X-Murmurly-Signature: t=1792420000,v1=5f2b…

{
  "id": "<delivery id>",
  "signal": "task.assigned",
  "version": …,
  "workspaceId": "…",
  "channel": "…",
  "sentAt": "2026-10-19T14:40:00Z",
  "recipient": { … },
  "payload": { … }
}
  • id The delivery id. Deliveries are at least once, so use it to skip retries you already handled.
  • signal Which update this is, for example task.assigned.
  • version The payload version for this signal.
  • workspaceId The workspace it came from.
  • sentAt When Murmurly sent it.
  • recipient Only on signals addressed to one person.
  • payload The update itself.
04

Signals

Switch them on under Signal Routing

Engagements

  • event.created
  • event.requested
  • event.status_changed
  • event.description_changed
  • event.cancelled
  • event.assignee_added

Guests and RSVPs

  • event_person.invited Essential
  • event_person.claimed
  • event_person.removed
  • event_rsvp.answered
  • event_rsvp.accepted
  • event_rsvp.declined
  • event_rsvp.confirmed

Tasks

  • task.assigned Essential

Conversations

  • thread.replied
  • thread.answered
  • thread.mentioned
  • thread.external
  • note.mentioned

People and organizations

  • person.assignee_added
  • organization.assignee_added

Fields and proposals

  • field.date_reached
  • field.member_linked
  • field.retention_review_due
  • proposal.decided
05

Limits and retries

2xx in 10 s Anything else, a redirect included, counts as failed.
Retried Failed deliveries are retried with backoff. Same id each time.
120 burst Then 600 an hour, per endpoint. Over it waits, never drops.
300 s Reject signatures older than this.
24 h overlap Rotate secret keeps the old key signing for 24 hours.
https only localhost, .local, .internal and private IPs are refused.

Need to send data in, too? The API takes requests from your systems.