Subscriptions v2
Subscriptions v2 represents the latest evolution of Bandwidth’s notification platform, designed to offer greater flexibility and clearer management of event streams. This version introduces a modernized API approach with improved organization of subscription definitions and delivery options.
Key highlights of Subscriptions v2 include:
- Subscription Definitions: Predefined event categories simplify understanding and choosing the events you want to track.
- Multiple Delivery Methods: Select between webhook callbacks for automated handling or email notifications for alerts and monitoring.
- Advanced Filtering: Apply filters on subscription criteria to receive only the most relevant event information.
- Account-Specific and System-Wide Support: Easily subscribe to events scoped to your specific accounts or to global system events.
- HMAC: Enhanced security through HMAC signing of webhook payloads
Subscriptions v2 is fully supported alongside the existing version, giving you flexibility to adopt new capabilities at your own pace.
In the sections ahead, you will learn how to create, manage, and optimize your subscriptions with the new platform to enhance your event-driven integrations.
Contents
- Designing Notification Webhooks
- Idempotency and Ordering
- Firewalls, IP Allowlisting, and HMAC
- Subscription Blocking Behaviour
- Webhook Port Requirements
- HMAC Signature Implementation
- Subscription Types
- Bandwidth App Order Update
- Order Change Events
- Note Events
- Number Reputation Management Monitoring Update
- Monitoring State Change Events
- Bandwidth App Order Update
Designing Notification Webhooks
Before configuring subscriptions, consider how your webhook receiver will handle the inbound traffic. The sections below cover the most common operational concerns.
Idempotency and Ordering
Bandwidth delivers notifications asynchronously and at least once. Events are not guaranteed to arrive in the order they occurred, and the same event may be delivered more than once. How you handle this depends on what your application needs from the event stream.
Pattern 1 — Latest state only. If your application only cares about the current status of an entity (e.g., the current order status), design your handler to be idempotent — processing the same event twice should produce the same result as processing it once. A practical approach is to record the orderId and lastModifiedDate of each received event and skip processing when you have already handled a newer update for that order. Out-of-order or duplicate deliveries are then harmless.
Pattern 2 — Full history. If your application needs a complete audit trail of all state transitions, always write every event to your store — but apply logical ordering using lastModifiedDate before displaying or acting on the sequence. This ensures the history is complete even if events arrive out of order.
Notification-type-specific logic. Each subscription definition produces a distinct payload structure — not all notifications include fields like orderId or lastModifiedDate. Review the payload structure for each event type you subscribe to and apply idempotency and ordering logic appropriate to that specific type.
Always respond with a 2xx status code immediately upon receiving a webhook, before performing any downstream processing. Bandwidth treats non-2xx responses and connection timeouts as delivery failures and may retry the notification. Retries use an exponential back-off strategy; if failures persist beyond the retry window, the subscription may be blocked to prevent further accumulation of undeliverable notifications (see Subscription Blocking Behaviour below). Performing slow operations synchronously in the request handler increases the risk of timeouts triggering this retry cycle — enqueue the payload for asynchronous processing instead.
Firewalls, IP Allowlisting, and HMAC
If your webhook endpoint sits behind a firewall, you may need to allowlist the Bandwidth egress IP ranges to ensure deliveries are not blocked. See New Source IP Addresses for Bandwidth App Webhook Notifications for the current list of source IP addresses used for webhook delivery.
For payload authenticity, Bandwidth strongly recommends enabling HMAC signing on your v2 subscriptions. Every webhook delivery includes an X-Bandwidth-Signature-SHA-256 header containing a base64-encoded HMAC-SHA256 signature of the request body. Verifying this signature on your receiver ensures that the request genuinely originated from Bandwidth and has not been tampered with in transit.
See HMAC Signature Implementation below for step-by-step verification instructions and example code.
Subscription Blocking Behaviour
Bandwidth monitors webhook delivery health for each subscription. If your endpoint returns repeated failures — either non-2xx responses or connection timeouts — the subscription will be automatically blocked to prevent the accumulation of undeliverable notifications.
Why it blocks: A blocked subscription protects both parties from unbounded retry storms. It signals that the receiving endpoint is unavailable or rejecting messages, and further delivery attempts would consume resources on both sides without benefit.
How it blocks: After repeated consecutive delivery failures, Bandwidth stops attempting delivery. Notifications generated while a subscription is blocked are retained for several weeks — once the endpoint is confirmed active via a successful test notification, Bandwidth will retroactively process the backlog of held notifications.
How to recover: Once your endpoint is back online and returning 2xx responses, use the Send Test Notification API (POST /v2/subscriptions/{subscriptionId}/notifications) or the Test subscription action in the Bandwidth App to confirm reachability. A successful test triggers Bandwidth to unblock the subscription and begin processing the retained notification backlog. You can review past delivery attempts using the List Notifications API (GET /v2/subscriptions/{subscriptionId}/notifications) or Notification history in the Bandwidth App.
Webhook Port Requirements
Bandwidth's network security policy permits outbound webhook traffic only to standard HTTPS ports. Configuring a webhook URL that targets a non-standard port (e.g., https://your-domain.com:8123/webhook) will fail to deliver because our network security team does not allow public egress traffic to non-standard ports by default.
If your integration requires a non-standard port, a firewall exception must be opened by the Bandwidth network security team. Reach out to Bandwidth Support to request an exception before configuring the subscription. Once the exception is in place, use the Send Test Notification API (POST /v2/subscriptions/{subscriptionId}/notifications) to confirm end-to-end connectivity before relying on the subscription in production.
HMAC Signature Implementation
To ensure the security of webhook payloads, Bandwidth uses HMAC signing. This allows receivers to verify the authenticity of the payloads received from Bandwidth.
How It Works
Bandwidth generates an HMAC signature using a secret key and the payload body. The base64-encoded signature is included in the X-Bandwidth-Signature-SHA-256 header of the webhook request. Consumers can use this signature to verify the integrity and authenticity of the payload.
The signature is highly sensitive to the exact formatting of the payload, including whitespace and character encoding. Any changes to the payload body, even minor ones, will result in a different signature and cause verification to fail. Always use the raw request body for signature validation.
Steps to Verify the HMAC Signature
- Retrieve the Signature: Extract the
X-Bandwidth-Signature-SHA-256header from the webhook request. - Generate the Signature Locally:
- Use the same secret key as provided to Bandwidth.
- Hash the received body using the HMAC algorithm (SHA256).
- Compare Signatures: Compare the locally generated signature with the one provided in the
X-Bandwidth-Signature-SHA-256header. If they match, the payload is authentic.
Example POST Request
POST https://your-domain.com/webhooks/your-endpoint
Authorization: Basic aGVsbG86d29ybGQ=
Content-Type: application/json
X-Bandwidth-Signature-SHA-256: AbCdEfGhIjKlMnOpQrStUvWxYz1234567890==
{
"completedPhoneNumbers": [
"+19195555298"
],
"lastModifiedDate": "2025-05-05T14:08:26.103Z",
"message": "Created a new number order for 1 number from RALEIGH, NC",
"orderId": "9cf8daa0-89a4-46aa-a1aa-8b5cf621f218",
"orderType": "orders",
"status": "COMPLETE"
}
Example Verification Code (Python)
import base64
import hashlib
import hmac
def hmac_256(key, data):
"""Generate HMAC SHA256 hash."""
return hmac.new(key.encode(), data.encode(), hashlib.sha256).digest()
def validate_signature(received_signature, request_body, secret):
"""Verify the HMAC signature with the given secret."""
generated_signature = base64.b64encode(hmac_256(secret, request_body)).decode()
return hmac.compare_digest(received_signature, generated_signature)
# Usage
secret = 'your_shared_secret'
received_signature = 'received_signature'
request_body = 'body_of_the_request'
is_valid = validate_signature(received_signature, request_body, secret)
if is_valid:
print('The webhook is valid.')
else:
print('Invalid webhook delivery.')
Subscription Types
Bandwidth App Order Update
The Bandwidth_App_Order_Update subscription definition can be used to receive webhooks for updates to orders created within the Bandwidth App. These can be filtered by a few properties, including the following:
orderType - The type of the order for which the notification is created, such as orders or disconnects
eventType - The type of event that happened for the order, either order_change for order status changes or note for a note being added to the order
orderId - The order ID generated by the Bandwidth App for a specific order or orders, useful for when you only want notifications about a specific subset of orders
status - The order status based on which to filter order change notifications, such as PROCESSING, COMPLETE, PARTIAL, MISSING_REQUIREMENTS, or FAILED. This can be omitted if you’d like to receive all notifications for all statuses
These attributes can be specified in the filters field of the request passed to the POST /subscriptions endpoint. If provided, you will only receive order notifications matching the specified filters. This can be omitted if you’d like to receive all notifications for all order types on your account.
The fields for order change and note notifications can differ slightly, but share the same general structure. The following subsections break down what you can expect to receive for each event type.
Order Change Events - Structure
These events represent a change in the status of an order submitted within the Bandwidth App, such as an order moving to a completed status once all work associated with the order has finished.
| Field | Type | Description | Example(s) | Required |
|---|---|---|---|---|
| lastModifiedDate | string (date-time) | The last time the order was modified. | 2025-05-05T14:08:26.103Z | Yes |
| message | string | A human-readable message describing the update. | Created a new number order for 1 number from RALEIGH, NC | Yes |
| orderId | string (UUID) | Unique identifier for the order generated by Bandwidth. | 9cf8daa0-89a4-46aa-a1aa-8b5cf621f218 | Yes |
| orderType | string | The type of the order. | orders, disconnects | Yes |
| status | string | Current status of the order. The possible statuses will reflect those of the order type the notification is for. | RECEIVED, PROCESSING, COMPLETE, PARTIAL, MISSING_REQUIREMENTS, FAILED | Yes |
| customerOrderId | string | Customer’s reference ID for the order. This is only sent if one was provided during order creation. | customer-order-id | No |
| completedPhoneNumbers | Array of strings | List of successful phone numbers in the order in E.164 format. This is only present for orders in a terminal status. | ["+19195555298"] | No |
| partialPhoneNumbers | Array of strings | List of phone numbers for which only some of the services got activated, in E.164 format. This is only present for orders in a terminal status. | ["+19195555291"] | No |
| processingPhoneNumbers | Array of strings | List of phone numbers which are under processing, in the order in E.164 format. This is only present for orders in a non-terminal status. | ["+19195555292"] | No |
| missingRequirementsPhoneNumbers | Array of strings | List of phone numbers for which some services could not be activated due to missing requirements, in the order in E.164 format. This is only present for orders in a non-terminal status. | ["+19195555293"] | No |
| phoneNumbers | Array of strings | List of phone numbers in the order in E.164 format. This is only present for orders in a non-terminal status. | ["+19195555294"] | No |
Order Change Events - Payload Example - Order Completed
{
"completedPhoneNumbers": ["+19195555298"],
"lastModifiedDate": "2025-05-05T14:08:26.103Z",
"message": "Created a new number order for 1 number from RALEIGH, NC",
"orderId": "9cf8daa0-89a4-46aa-a1aa-8b5cf621f218",
"orderType": "orders",
"status": "COMPLETE"
}
Order Change Events - Payload Example - Order Partial status
{
"partialPhoneNumbers": ["+19195555291"],
"lastModifiedDate": "2025-05-05T14:08:26.103Z",
"message": "Created a new number order for 1 number from RALEIGH, NC",
"orderId": "9cf8daa0-89a4-46aa-a1aa-8b5cf621f218",
"orderType": "orders",
"status": "PARTIAL"
}
Order Change Events - Payload Example - Order in Processing status
{
"processingPhoneNumbers": ["+19195555292"],
"lastModifiedDate": "2025-05-05T14:08:26.103Z",
"message": "Created a new number order for 1 number from RALEIGH, NC",
"orderId": "9cf8daa0-89a4-46aa-a1aa-8b5cf621f218",
"orderType": "orders",
"status": "PROCESSING"
}
Order Change Events - Payload Example - Order in Missing Requirements status
{
"missingRequirementsPhoneNumbers": ["+19195555293"],
"lastModifiedDate": "2025-05-05T14:08:26.103Z",
"message": "Created a new number order for 1 number from RALEIGH, NC",
"orderId": "9cf8daa0-89a4-46aa-a1aa-8b5cf621f218",
"orderType": "orders",
"status": "MISSING_REQUIREMENTS"
}
Order Change Events - Payload Example - Order Submitted
{
"phoneNumbers": ["+19195555298"],
"lastModifiedDate": "2025-05-05T14:08:26.103Z",
"message": "We have received a port out request for the following number(s).",
"orderId": "9cf8daa0-89a4-46aa-a1aa-8b5cf621f218",
"orderType": "portouts",
"status": "NEW"
}
Note Events - Structure
These events represent a note being added to an order within the Bandwidth App. This may be a note generated by the system or a note added by a user within your organization or Bandwidth in order to communicate about the status of an order.
| Field | Type | Description | Example(s) | Required |
|---|---|---|---|---|
| lastModifiedDate | string (date-time) | The last time the order was modified. | 2025-05-05T14:08:26.103Z | Yes |
| note | string | The text of the note which was added to the order. | Service activation order was created with ID: 877bf791-fbe0-4448-b9af-bd2b59d7f3d0 | Yes |
| orderId | string (UUID) | Unique identifier for the order generated by Bandwidth. | 9cf8daa0-89a4-46aa-a1aa-8b5cf621f218 | Yes |
| orderType | string | The type of the order. | orders disconnects | Yes |
| customerOrderId | string | Customer’s reference ID for the order. This is only sent if one was provided during order creation | customer-order-id | No |
Note Events - Example
{
"lastModifiedDate": "2025-05-05T14:08:26.103Z",
"note": "Service activation order was created with ID: 877bf791-fbe0-4448-b9af-bd2b59d7f3d0",
"orderId": "9cf8daa0-89a4-46aa-a1aa-8b5cf621f218",
"orderType": "orders"
}
Number Reputation Management Monitoring Update
The Number_Reputation_Management_Monitoring_Update subscription definition can be used to receive webhooks for updates to the monitoring states of numbers under monitoring via the Number Reputation Management product within the Bandwidth App. These can be filtered by a few properties, including the following:
phoneNumber - The phone number being monitored, in e164 format.
groupName - The reputation group within Number Reputation Management that the number is assigned to.
groupId - The ID for the reputation group within Number Reputation Management that the number is assigned to.
carriersReputationSummary - The status of the number from our standard monitoring sources.
consumerAppsReputationSummary - The status of the number from our expanded monitoring sources. This requires that the number be on an account that has expanded monitoring enabled. Otherwise it may be omitted.
customerId - The ID of the customer of the Bandwidth App account that is tied to the number. This is relevant to reseller users and their customers. Otherwise it may be omitted.
These attributes can be specified in the filters field of the request passed to the POST /subscriptions endpoint. If provided, you will only receive order notifications matching the specified filters. This can be omitted if you’d like to receive all notifications for all numbers monitored on your account.
The following subsections break down what you can expect to receive for each event type.
Monitoring State Change Events - Structure
| Field | Type | Description | Example(s) | Required |
|---|---|---|---|---|
| phoneNumber | string | The phone number being monitored. | +19805551234 | Yes |
| groupName | string | The group that the phone number is assigned to. | Group 1 | Yes |
| groupId | int | The ID assigned to the monitoring group. | 12 | Yes |
| carriersReputationSummary | string | Current status of the number. Can be either FLAGGED or CLEAN. | CLEAN, FLAGGED | Yes |
| consumerAppsReputationSummary | string | Current expanded monitoring status of the number. Can be either FLAGGED or CLEAN. | CLEAN, FLAGGED | No |
| customerId | int | The ID of a customer of the reseller account. | 15 | No |
| accountId | int | Bandwidth App account ID on which NRM is enabled for this number. | 1234567 | Yes |
Monitoring State Change Events - Example
{
"phoneNumber": "+19805551234",
"groupName": "Group 1",
"groupId": 12,
"carriersReputationSummary": "CLEAN",
"consumerAppsReputationSummary": "FLAGGED",
"customerId": 15,
"accountId": 1234567
}
Messaging Lost
The Messaging_Lost subscription definition can be used to receive notifications when one or more of your telephone numbers have lost their messaging service. This occurs when an external number is ported in to another account, transferring the messaging service away from your account.
Messaging Lost - Structure
| Field | Type | Description | Example(s) | Required |
|---|---|---|---|---|
| accountId | int | The Bandwidth App account ID from which messaging service was lost. | 753 | Yes |
| impactedTelephoneNumbers | Array of strings | List of telephone numbers that have lost messaging service due to a port-in. | ["+12018391100", "+12018391101"] | Yes |
Messaging Lost - Example
{
"accountId": 753,
"impactedTelephoneNumbers": ["+12018391100", "+12018391101"]
}
Requirements Package Updates
The Requirement_Package_Updates subscription definition can be used to receive webhooks when the status of a requirements package is updated.
Requirements Package Updates - Structure
| Field | Type | Description | Example(s) | Required |
|---|---|---|---|---|
| accountId | int | The Bandwidth account ID associated with the requirements package. | 6555555 | Yes |
| countryCodeA3 | string | Country code of the requirements package address in ISO 3166-1 alpha-3 format. | NLD | Yes |
| customReference | string | Your custom reference for the requirements package. | Custom reference | Yes |
| dateOfApproval | string (date-time), nullable | The date and time the requirements package was approved. This is null if the requirements package has not yet been approved. | 2026-06-23T16:38:50.399Z | Yes |
| dateOfLastUpdate | string (date-time) | The date and time the requirements package was last updated. | 2026-06-23T16:38:50.399Z | Yes |
| endUserType | string | Type of end user. | RESIDENTIAL, BUSINESS, SOLE_PROPRIETOR | Yes |
| phoneNumberType | string | Type of the phone number. | GEOGRAPHIC, NATIONAL, MOBILE, TOLL_FREE, SHARED_COST | Yes |
| remarks | string | Remarks provided by the user or admin regarding the requirements package. This is N/A if no remarks have been provided. | N/A | Yes |
| requirementsPackageId | string (UUID) | Unique identifier for the requirements package. | 05ee51fd-49cf-4e85-8a41-abb7f8f1b017 | Yes |
| requirementsPackageUrl | string (URI) | The URL to view the requirements package in the Bandwidth Dashboard. | https://app.bandwidth.com/a/6555555/servicemanagement/end-user-validation/05ee51fd-49cf-4e85-8a41-abb7f8f1b017 | Yes |
| oldStatus | string | The status of the requirements package prior to this update. | DRAFT, SUBMITTED, VERIFIED, VERIFICATION_FAILED, DISABLED, AUTO_VALIDATED | Yes |
| status | string | The current status of the requirements package. | DRAFT, SUBMITTED, VERIFIED, VERIFICATION_FAILED, DISABLED, AUTO_VALIDATED | Yes |
Requirements Package Updates - Example
{
"accountId": 6555555,
"countryCodeA3": "NLD",
"customReference": "Custom reference",
"dateOfApproval": "2026-06-23T16:38:50.399Z",
"dateOfLastUpdate": "2026-06-23T16:38:50.399Z",
"endUserType": "RESIDENTIAL",
"phoneNumberType": "GEOGRAPHIC",
"remarks": "N/A",
"requirementsPackageId": "05ee51fd-49cf-4e85-8a41-abb7f8f1b017",
"requirementsPackageUrl": "https://app.bandwidth.com/a/6555555/servicemanagement/end-user-validation/05ee51fd-49cf-4e85-8a41-abb7f8f1b017",
"oldStatus": "SUBMITTED",
"status": "AUTO_VALIDATED"
}