Receiving Webhooks

Last updated: June 2, 2026

Overview

Switchboard can send real-time HTTP POST notifications (webhooks) to a URL you control whenever certain events occur — for example, when a broadcast changes status, when a donation is processed, or when an incoming text message is received on one of your sending numbers. This allows you to build automated workflows like triggering a follow-up action when a broadcast finishes sending, syncing donation data to an external CRM, alerting your team when a broadcast encounters an error, or processing incoming replies in an external system.

We currently support webhook events for Broadcasts, Contributions, and Incoming Phone Messages, and we're adding support for new types of events over time. If there are other types of events you're interested in receiving, please reach out to support@oneswitchboard.com.

Setup

For now, Webhooks are configured in your Switchboard organization's settings onunder Organization Settings > Webhooks. To get started, you will need:

  1. Destination URL — An HTTPS endpoint you control that can accept POST requests (e.g., https://yourserver.com/webhooks/switchboard).

  2. HTTP Basic Auth credentials (optional) — A username and password that Switchboard will include with every request so you can verify the request is authentic.

  3. Which events to subscribe to — See the event list below. You can subscribe to individual event types or all broadcast events (broadcast.*). You will be able to adjust this in the future.

To create a new webhook endpoint:

  1. Click New Endpoint.

  2. Enter a Name — a description to help you identify this endpoint.

  3. Enter a Destination URL — An HTTPS endpoint you control that can accept POST requests (e.g., https://yourserver.com/webhooks/switchboard).

  4. HTTP Basic Auth credentials — Optionally enable Authentication — check "Use Basic Authentication" and provide a username and password that Switchboard will include with every request so you can verify the request is authentic.

  5. Select a Webhook Version — see below for more.

  6. Choose which events to subscribe to — See the Events section below. You can subscribe to individual event types or all broadcast events (broadcast.*). You will be able to adjust this in the future, category wildcards (e.g., broadcast.*), or all events (`*`).

  7. Click Create.

Managing Endpoints

From the Webhooks settings page you can:

  • Edit an endpoint's name, URL, authentication, version, or event subscriptions by clicking the edit button.

  • Enable / Disable an endpoint — disabling an endpoint stops event delivery without deleting it. You can re-enable it at any time.

  • Delete an endpoint permanently.

  • View recent events — click an endpoint row to see its recent delivery history. Click an individual event to inspect the full payload, request sent, response received, delivery attempts, and any error messages.

Authentication

Authentication is optional but recommended. When enabled, every webhook request includes an Authorization: Basic <base64(username:password)> header using the credentials you provide during setup. You should validate this header on every incoming request to ensure the request is genuinely from Switchboard.

Webhook Versions

When creating a webhook endpoint, you choose a version that determines which event types are available and how payloads are formatted. Each configured endpoint can only receive events of a single version type. However, you can set up multiple Switchboard endpoints with the same destination URL, if you want to distinguish them on your end.

v1 (Default)

The default version that includes a broad set of events across all platforms. It currently supports Broadcast, Contribution events (from any platform), and Phone Message events.

ActBlue Compatible

A specialized version that includes Count On Me contribution events (contribution.succeeded, contribution.refunded, contribution.cancelled) formatted to match ActBlue's payload structure. This is designed to support existing workflows that already consume ActBlue webhooks — you can point Count On Me webhooks at Switchboard's webhook with minimal changes.

Events

Broadcast Events

There are four categories of broadcast webhook events:

  1. broadcast.created

    • Fired when a new broadcast is created (either from scratch or by copying an existing one).

    • The meta.details field contains:

      • created_source: "new" or "copy"

    • When a broadcast is copied, the data array contains two objects: the newly created broadcast and the original broadcast it was copied from.

  2. broadcast.status_changed

    • Fired when a broadcast transitions between statuses.

    • The meta.details field contains:

      • status_change_reason: one of the following values:

        Reason

        What happened

        began_sending

        Broadcast started sending (either manually or when a scheduled time was reached)

        completed

        All messages in the broadcast have been processed

        paused

        Broadcast was paused by a user

        unpaused

        A paused broadcast was resumed

        stopped

        Broadcast was permanently stopped

        recovered

        A previously completed broadcast resumed more messages (rare, internal recovery)

  3. broadcast.schedule_changed

    • Fired when a broadcast's scheduled send time is set or changed.

    • The meta.details field contains:

      • schedule_change_reason: "scheduled", "schedule_updated", "resume_scheduled", or "resume_updated"

      • scheduled_for: ISO 8601 timestamp of the scheduled time, or null if the schedule was cleared

  4. broadcast.error_occurred

    • Fired when a broadcast fails pre-send validation (e.g., the broadcast can't be sent due to a configuration issue).

    • The meta.details field contains:

      • error_description: A human-readable description of what went wrong

Unified Contribution Events

Unified contribution events provide a single stream of contribution activity across all connected platforms (ActBlue, Count on Me, etc.), so you don't need to set up separate integrations for each source.

  • unified_contribution.created

    • Fired when Switchboard receives a contribution event from any platform.

Incoming Message Events

  1. phone_message.received

    • Fired when an incoming text message is received on a sending number in your organization.

    • This event has no meta.details field (details will be null).

    • The meta.actor field will be noreply@oneswitchboard.com since incoming messages are system events, not user-initiated.

    • The data array contains a single object with object_type set to "PhoneMessage" and the full incoming message data in the object field.

Actblue Compatible Contribution Events

Contribution events fire when donations processed through Count On Me change state. These events are available specifically in the ActBlue Compatible webhook version.

  • contribution.succeeded

    • Fired when a contribution is successfully processed.

  • contribution.refunded

    • Fired when a contribution is refunded.

  • contribution.cancelled

    • Fired when a recurring contribution is cancelled.

Wildcard Subscriptions

  • Subscribe to broadcast.* to receive all four broadcast event types, aboveincluding new broadcast events added in the future.

  • Subscribe to * to receive all events across all categories, including new event types and categories added in the future.

Payload Format

Every webhook is delivered as an HTTP POST with a JSON body matching this structure:

{
  "meta":{
    "created_at":"2026-03-19T14:30:00.000000+00:00",
    "event_id":"wevt_01JFEXAMPLE",
    "event_type":"broadcast.status_changed",
    "environment":"production",
    "actor":"user@example.com",
    "details":{
      "status_change_reason":"completed"
    }
  },
  "data":[
    {
      "object_type":"Broadcast",
      "description":"completed",
      "id":"bc_01JFEXAMPLE",
      "api_url":"https://api.oneswitchboard.com/public/v1/broadcasts/bc_01JFEXAMPLE",
      "object":{ 
          // Full object — see "Broadcast Object" below for a sample    
       }
    }
  ]
}

Key Fields

Field

Description

meta.created_at

ISO 8601 timestamp of when the event was generated

meta.event_id

Unique identifier for this event (useful for deduplication)

meta.event_type

The event type string (e.g., broadcast.status_changed, contribution.succeeded, phone_message.received)

meta.environment

Always "production" for live events

meta.actor

Email of the user who triggered the action, or noreply@oneswitchboard.com for system-initiated events (e.g., a scheduled send, auto-completion, or incoming message, or contribution)

meta.details

Event-specific metadata (varies by event type — see above). null for events with no additional details.

data[].object_type

"Broadcast" for broadcast events, "Contribution" for contribution events, "PhoneMessage" for incoming message events

data[].id

The object's public API ID

data[].api_url

Direct link to the object in the public API

data[].object

The full resource — see data object documentation below

Data Objects

Broadcast Object

The data[].object for broadcast events contains the full broadcast resource, serialized identically to the public API response. See the Broadcasts API documentation for the complete field reference.

Unified Contribution Object

The data[].object for unified_contribution.created events contains the contribution data as received from the originating platform.

Example unfied_contribution.created Payload

{
  "meta": {
    "created_at": "2026-05-01T18:32:15.123456+00:00",
    "event_id": "wevt_a1b2c3d4e5f6",
    "event_type": "unified_contribution.created",
    "environment": "production",
    "actor": "noreply@oneswitchboard.com",
    "details": null
  },
  "data": [
    {
      "object_type": "Contribution",
      "description": "Contribution received from donation",
      "id": "sbc_k7m9x2p4r8w1",
      "api_url": "/api/v1/contributions/sbc_k7m9x2p4r8w1/",
      "object": {
        "created_at": "2026-05-01T18:30:00+00:00",
        "paid_at": "2026-05-01T18:30:05+00:00",
        "order_number": "sbc_k7m9x2p4r8w1",
        "entity_id": 48291,
        "amount": "50.00",
        "message_id": null,
        "broadcast_id": null,
        "email_blast_id": null
      }
    }
  ]
}

Phone Message Object

The data[].object for phone_message.received events contains the incoming message with the following fields:

Field

Type

Description

broadcast_id

string or null

ID of the broadcast this message is associated with, if any. For incoming replies to a broadcast, this will be the broadcast's ID (e.g., "bc_abc123"). null if the message is not associated with a broadcast.

to_number

string or null

Phone number that received the message, in E.164 format (e.g., "+12345552368"). This is your organization's sending number.

from_number

string or null

Phone number that sent the message, in E.164 format (e.g., "+12345552368"). This is the subscriber's number.

status

string

The status of the message. For incoming messages, this will typically be "received".

error_code

string or null

Switchboard error code, if applicable. Typically null for incoming messages.

error_message

string or null

Switchboard error message, if applicable. Typically null for incoming messages.

provider_error_code

string or null

Error code from the upstream SMS provider, if applicable.

provider_error_details

object or null

JSON details from the upstream SMS provider, if applicable.

skip_reason

string or null

Reason the message was skipped, if applicable. Typically null for incoming messages.

message_type

string

The type of message: "sms", "mms", or "unknown".

message_direction

string

The direction of the message: "inbound" (from contact to organization) or "outbound" (from organization to contact). For phone_message.received events, this will always be "inbound".

phone_type

string

The type of phone that sent the message: "mobile", "landline", "voip", or "unknown".

sent_time

string or null

ISO 8601 timestamp of when the message was sent.

received_at

string or null

ISO 8601 timestamp of when the message was received. Only populated for inbound messages.

is_opt_out

boolean

true if this message is an opt-out request (e.g., the subscriber texted "STOP").

is_phone_opted_out

boolean

true if the sender's phone number is currently opted out from receiving messages.

clicks

integer

Number of Switchboard-tracked link clicks associated with this message. Typically 0 for incoming messages.

donations

integer

Number of donations tracked via this message. Typically 0 for incoming messages.

donation_amount

string

Dollar amount of donations tracked via this message (e.g., "0.00").

replies

integer

Number of replies associated with this message.

carrier

string or null

Name of the phone carrier for the sender's number (e.g., "T-Mobile", "Verizon").

text

string or null

The body of the incoming message.

media_urls

array

List of URLs for any media (images, etc.) included with the message. Empty array if no media.

Example phone_message.received Payload

{
  "meta":{
    "created_at":"2026-03-19T14:32:15.000000+00:00",
    "event_id":"wevt_01JFEXAMPLE2",
    "event_type":"phone_message.received",
    "environment":"production",
    "actor":"noreply@oneswitchboard.com",
    "details":null
  },
  "data":[
    {
      "object_type":"PhoneMessage",
      "description":"Received incoming phone message",
      "id":"pm_01JFEXAMPLE",
      "api_url":"https://api.oneswitchboard.com/v1/phone_messages/pm_01JFEXAMPLE",
      "object":{
        "broadcast_id":"bc_01JFEXAMPLE",
        "to_number":"+12025551234",
        "from_number":"+13105559876",
        "status":"received",
        "error_code":null,
        "error_message":null,
        "provider_error_code":null,
        "provider_error_details":null,
        "skip_reason":null,
        "message_type":"sms",
        "message_direction":"inbound",
        "phone_type":"mobile",
        "sent_time":"2026-03-19T14:32:14.000000+00:00",
        "received_at":"2026-03-19T14:32:15.000000+00:00",
        "is_opt_out":false,
        "is_phone_opted_out":false,
        "clicks":0,
        "donations":0,
        "donation_amount":"0.00",
        "replies":0,
        "carrier":"T-Mobile",
        "text":"Yes I'd like to volunteer!",
        "media_urls":[ ]
      }
    }
  ]
}

ActBlue Compatible Webhooks

The data[].object for contribution events contains the full contribution resource.

Example contribution.succeeded Payload

{
  "donor": {
    "firstname": "Jane",
    "lastname": "Doe",
    "addr1": "123 Main St",
    "city": "Springfield",
    "state": "IL",
    "zip": "62701",
    "country": "US",
    "isEligibleForExpressLane": null,
    "employerData": {
      "employer": "Acme Corp",
      "occupation": "Software Engineer",
      "employerAddr1": "456 Tech Blvd",
      "employerCity": "Chicago",
      "employerState": "IL",
      "employerCountry": "US"
    },
    "email": "jane.doe@example.com",
    "phone": "+12175551234"
  },
  "contribution": {
    "createdAt": "2026-05-01T18:30:00Z",
    "orderNumber": "sbc_k7m9x2p4r8w1",
    "contributionForm": "support-our-cause",
    "refcode": "spring2026",
    "refcode2": null,
    "refcodes": {
      "refcode": "spring2026"
    },
    "recurringPeriod": "monthly",
    "isRecurring": true,
    "isMobile": false,
    "uniqueIdentifier": "sbc_k7m9x2p4r8w1",
    "status": "approved",
    "expressSignup": false,
    "textMessageOption": null,
    "recurringDuration": null,
    "weeklyRecurringSunset": null,
    "isPaypal": false,
    "isExpress": false,
    "withExpressLane": false,
    "creditCardExpiration": null,
    "abTestName": null,
    "abTestVariation": null,
    "thanksUrl": null,
    "retryUrl": null,
    "giftDeclined": null,
    "giftIdentifier": null,
    "shippingName": null,
    "shippingAddr1": null,
    "shippingCity": null,
    "shippingState": null,
    "shippingZip": null,
    "shippingCountry": null,
    "smartBoostAmount": null,
    "customFields": [],
    "merchandise": [],
    "bumpYourRecurring": null
  },
  "lineitems": [
    {
      "sequence": 1,
      "entityId": 48291,
      "amount": "50.00",
      "recurringAmount": "50.00",
      "paidAt": "2026-05-01T18:30:05Z",
      "lineitemId": "sbc_k7m9x2p4r8w1",
      "committeeName": "Friends of Springfield",
      "fecId": null,
      "paymentId": null
    }
  ],
  "form": {
    "name": "Support Our Cause",
    "kind": "page",
    "managingEntityName": null,
    "managingEntityCommitteeName": null,
    "ownerEmail": null
  }
}

Delivery and Retries

  • Webhooks are delivered asynchronously, typically within seconds of the event.

  • Your endpoint must respond with a 2xx status code within 10 seconds. Redirects are not followed.

  • If delivery fails (non-2xx response, timeout, or connection error), Switchboard retries up to 5 times with increasing delays.

  • Beyond that, a background process continues retrying failed deliveries for up to 24 hours.

  • After all retries are exhausted, the delivery is marked as failed. No further automatic retries occur.

  • You can monitor delivery status for each endpoint from the Webhooks settings page — click an endpoint to see its recent events, then click an individual event to inspect the payload, request, response, and any errors.

Recommendations for Your Endpoint

  1. Respond quickly. Return a 200 OK immediately and process the payload asynchronously. The 10-second timeout is strict.

  2. Handle duplicates. In rare cases (e.g., during retries), the same event may be delivered more than once. Use meta.event_id to deduplicate.

  3. Validate authentication. If you configured Basic Auth credentials, check the Authorization header on every request.

  4. Be prepared for out-of-order delivery. If events occur in rapid succession (e.g., a broadcast changes status quickly, or multiple messages arrive at once, or several donations come in together), webhooks may arrive in a different order than the events occurred. Use meta.created_at to determine the true sequence.

  5. Don't rely on the data[].object for final state. For critical workflows where the latest data is required, use the api_url to fetch the current state from the API directly after receiving a webhook, since the object snapshot represents the state at the moment the event was generated.