Skip to content
Miss Blue

Reference

Delivery

Project webhooks are signed and retried. A per-message status callback is retried too, but is not signed; pull the message by id when you need authoritative state.

List the endpoints this project sends events to.

Your key may manage its own project's endpoints and no others. Another project's id returns 404 rather than 403, because whether it exists is not something a stranger should learn.

curl -X GET https://api.missblue.dev/v1/projects/3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60/webhooks \
  -H "Authorization: Bearer $MISS_BLUE_KEY"

Response

{
  "total": 1,
  "data": [{
    "id": "b71c0d92-5a83-4e1f-9d70-6c2e8a4b3f15",
    "project_id": "3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60",
    "url": "https://example.com/hooks/miss-blue",
    "events": ["message.received", "group.updated", "contact.synced"],
    "enabled": true,
    "disabled_reason": null,
    "failure_streak": 0,
    "created_at": "2026-08-28T10:04:00Z"
  }]
}

Register where events should go.

Answers “tell me about everything on this line”: messages, typing, group changes, location requests, number profile changes, contacts, syncs, provisioning, calls, and campaigns — automation runs starting and finishing (automation.run.finished carries an outcome: completed, replied, stopped, number_left, gave_up or failed), announcements from review to finish with their counts, and scheduled messages sent, failed or cancelled. Every message event carries data.message.source: null for inbound, otherwise { type: automation | announcement | scheduled | api | console | mac } with the automation_id, run_id and step, the announcement_id, or the scheduled_id it came from. Every message event also carries data.conversation: { url, link_code, chat_id }, the conversation's short link (url is https://missblue.dev/c/<link_code>), so a handler can link to it without another call. The link opens only for somebody who may read the conversation. Message events put their fields under data.message on every line, Twilio included; group, location, number profile, contact sync and call events put their object under data.resource, beside data.number; contact.updated, number.provisioned, typing.indicator and campaign events put their fields directly under data. The call.started, answered, ended and failed events are sent for FaceTime Audio calls only for now; a phone call sends call.recording.ready when it was recorded. Omit events to receive all supported events, including ones added later; naming events delivers only those. The signing secret is returned only once. Each delivery is signed and retried with backoff; an endpoint that keeps failing is switched off and the reason is exposed.

url*https urlMust be https, and must not resolve to a private address.
eventsstring[]message.received, message.sent, message.delivered, message.failed, typing.indicator, group.created, group.updated, location.requested, number.profile.updated, contact.updated, contact.synced, number.provisioned, call.started, call.answered, call.ended, call.failed, call.recording.ready, automation.run.started, automation.run.finished, automation.review, announcement.awaiting_review, announcement.approved, announcement.rejected, announcement.started, announcement.finished, announcement.cancelled, scheduled.sent, scheduled.failed, or scheduled.cancelled. Omit for all.
curl -X POST https://api.missblue.dev/v1/projects/3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60/webhooks \
  -H "Authorization: Bearer $MISS_BLUE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/miss-blue",
    "events": [
      "message.received",
      "message.delivered",
      "group.updated",
      "contact.synced"
    ]
  }'

Response

{
  "id": "b71c0d92-5a83-4e1f-9d70-6c2e8a4b3f15",
  "project_id": "3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60",
  "url": "https://example.com/hooks/miss-blue",
  "events": ["message.received", "message.delivered", "group.updated", "contact.synced"],
  "enabled": true,
  "disabled_reason": null,
  "failure_streak": 0,
  "created_at": "2026-08-28T10:04:00Z",
  "secret": "whsec_…"
}

Keep anybody partway through one automation out of the others until it finishes.

On, a list or tag automation doesn't start for somebody who is in another of this project's: the one they are in finishes, and the other doesn't start for them then or later. An automation somebody starts by texting you (a keyword, a first message, any message) still answers them. Applies from the next start; nobody already in two is taken out of either. Off for every project until changed. Project admins and a key belonging to this project may change it; the project's detail says what it is.

curl -X POST https://api.missblue.dev/v1/projects/3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60/settings/one-automation-at-a-time \
  -H "Authorization: Bearer $MISS_BLUE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "one_automation_at_a_time": true
  }'

Response

{
  "project_id": "3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60",
  "one_automation_at_a_time": true
}

Automatically start typing after an inbound iMessage.

When enabled, each inbound one-to-one iMessage immediately starts a bounded native typing indicator while your responder prepares a reply. SMS, RCS, group chats, action messages, and hidden messages never trigger it. Project admins and a key belonging to this project may change the setting.

curl -X POST https://api.missblue.dev/v1/projects/3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60/settings/auto-typing-indicator \
  -H "Authorization: Bearer $MISS_BLUE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "auto_typing_indicator": true
  }'

Response

{
  "project_id": "3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60",
  "auto_typing_indicator": true
}

Switch an endpoint on or off without deleting it.

An endpoint that keeps failing is switched off by us for the same reason you would switch one off during a deploy: to stop hammering something that is not listening. Turning it back on is this call.

enabled*boolean
curl -X POST https://api.missblue.dev/v1/projects/3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60/webhooks/b71c0d92-5a83-4e1f-9d70-6c2e8a4b3f15/enabled \
  -H "Authorization: Bearer $MISS_BLUE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true
  }'

What we tried to send you, and what came back.

Every attempt, its response status, and how many retries it took. This is the first place to look when your endpoint is up but you are not seeing events — it distinguishes “we never sent it” from “we sent it and your server said 500”.

curl -X GET https://api.missblue.dev/v1/projects/3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60/webhooks/b71c0d92-5a83-4e1f-9d70-6c2e8a4b3f15/deliveries \
  -H "Authorization: Bearer $MISS_BLUE_KEY"

Response

{
  "total": 1,
  "data": [{
    "id": "d91d6b51-8639-43ad-8909-fd0439ecb80e",
    "event": "group.updated",
    "status": "delivered",
    "attempts": 1,
    "response_status": 204,
    "response_body": "",
    "created_at": "2026-08-28T10:04:00Z",
    "delivered_at": "2026-08-28T10:04:01Z"
  }]
}

Stop sending to an endpoint.

curl -X DELETE https://api.missblue.dev/v1/projects/3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60/webhooks/b71c0d92-5a83-4e1f-9d70-6c2e8a4b3f15 \
  -H "Authorization: Bearer $MISS_BLUE_KEY"
NextErrorsOne shape, and what each status means.