# Understanding webhook

The message status webhook allows you to programmatically track the delivery status of messages you send.

To receive the status update notifications in your application, you have to create a webhook configuration using the `webhook` property in the [`send`](/products/sms/enterprise-documentation/developer-documentation/api-references/send) methods.
Each SMS message can be linked to a specific [webhook configuration](/products/sms/enterprise-documentation/developer-documentation/wb/wb-configuration).

Example:


```json
{
  "destination": "+1234567890",
  "text": "Your verification code is: 123456",
  "reference": {
    "service": "AUTHENTICATION",
    "action": "2FA",
    "key": "user-12345"
  },
  "detailed": true,
  "webhook": {
    "url": "https://example.com/callback",
    "authentication": {
        "type": "NONE"
    }
  }
}
```

Check the [webhook configuration](/products/sms/enterprise-documentation/developer-documentation/wb/wb-configuration) page for more details.

When the message is accepted by the provider, the `send` API returns `200` (`OK`). The message status is immediately changed to `PROVIDER_ACCEPTANCE` and the webhook configuration is created.

No webhook notification is sent for the initial `PROVIDER_ACCEPTANCE` status.

### Status Change Notifications

The system sends a notification via webhook for every change.


```mermaid
sequenceDiagram
    participant App as Your Application
    participant Gateway as SMS Gateway
    participant Provider as Provider
    participant Carrier as Carrier
    participant Handset as Handset

    App->>Gateway: Send an SMS
    Gateway->>Provider: Send an SMS
    
    Provider-->>Gateway: PROVIDER_ACCEPTANCE
    Gateway-->>App: HTTP Status = 200
    Note over Gateway,App: Status = PROVIDER_ACCEPTANCE
    
    Provider->>Carrier: SMS
    Carrier->>Handset: SMS
    
    Handset-->>Carrier: Status Callback
    Carrier-->>Provider: Status Callback
    Provider-->>Gateway: Status Callback
    Gateway-->>App: Message Status Webhook
```

The system sends status notifications as HTTP `POST` requests to your configured webhook URL.

Example:


```
{
  "id": "2bf9a88a-29f2-438a-89a1-9a13bf03d03f",
  "webhookConfigurationId": "2dab9410-d2c8-4b60-bcb0-c5eeb073e71d",
  "type": "STATUS_UPDATE",
  "createdAt": "2026-02-12T14:51:00Z",
  "message": {
    "id": "b31b6607-9c55-48ba-b145-3f40b809d2d2",
    "providerAcceptanceAt" : "2026-02-12T14:49:00Z",
    "statusChangedAt": "2026-02-12T14:50:35Z",
    "reference": {
      "service": "DIGITALSIGN",
      "action": "AUTH",
      "key": "webhook_3"
    },
    "channel": "SMS",
    "status": "DELIVERED",
    "provider": {
      "name": "MITTO",
      "id": "ee055245-afa5-4dd0-8d8f-10fec8bf64a3",
      "status": "DELIVERED",
      "code": "0"
    },
    "sms": {
      "segments": 1
    }
  }
}
```

The webhook contains the following info:

* **id**: Unique identifier of the notification sent via webhook.
* **webhookConfigurationId** : Webhook configuration identifier
* **type**: Event type (always `STATUS_UPDATE` for status notifications)
* **createdAt**: Date and time in ISO 8601 format (e.g., `2026-02-12T14:51:00Z`)
* **message**: Message information object:
  * **id**: Message identifier
  * **providerAcceptanceAt**: Timestamp when the message was accepted by the provider (ISO 8601 format, e.g., 2026-02-12T14:51:00Z). Corresponds to the initial PROVIDER_ACCEPTANCE status.
  * **statusChangedAt**: Timestamp of the most recent status change (ISO 8601 format, e.g., 2026-02-12T14:51:05Z).
  * **reference**: Reference object specified in the send request
  * **channel** : Delivery channel. Possible values:
    * `SMS`
    * `RCS`
  * **status**: Current delivery status. Possible values:
    * `SENT` : Request sent by the provider
    * `DELIVERED` : Message delivered to handset
    * `REJECTED`: Request rejected by the provider. Message not sent.
    * `UNDELIVERED` : Message failed to deliver
    * `READ` : (RCS only) Message delivered and read by recipient
  * **provider**: Provider information
    * **name** : Provider name
    * **id**: Provider's message identifier
    * **status**: Provider-specific status
    * **code**: Provider status/error code
    * **message**: (Optional) Provider error details (e.g. `Number Blocked by Carrier`)
  * **sms**: SMS-specific information:
    * **segments** : Number of SMS segments used. Messages exceeding 160 characters (GSM-7) or 70 characters (UCS-2) are split into multiple segments.


### Webhook Retry Mechanism

**Expected Response:**

- HTTP status code: `200` (OK)
- Response timeout: 10 seconds
- Response body: Not required (ignored if present)


**Retry Behavior:**

The system retries failed webhook deliveries using exponential backoff:

| Attempt | Wait Time | Cumulative Time |
|  --- | --- | --- |
| 1 | 1 second | 1 second |
| 2 | 2 seconds | 3 seconds |
| 3 | 4 seconds | 7 seconds |
| 4 | 8 seconds | 15 seconds |
| ... | ... | ... |
| Final | - | 24 hours max |


**Retry Triggers:**

- HTTP status codes: 4xx (except 400, 401, 403), 5xx
- Network errors (timeout, connection refused, DNS failure)
- No response within 10 seconds


**After 24 Hours:**

- Retries stop permanently
- Notification is marked as failed
- No further attempts are made


**Handling Duplicate Notifications**

The same notification may be delivered multiple times due to retries or network issues.
Use the `id` field to detect and ignore duplicates:


```json
{
 "id": "2bf9a88a-29f2-438a-89a1-9a13bf03d03f",  // Use this as idempotency key
 "webhookConfigurationId": "2dab9410-d2c8-4b60-bcb0-c5eeb073e71d",
 ...
}
```

### Status Transition Diagram

The diagram below shows the status transitions of the messages for which the SMS Gateway sends a status notification request.


```mermaid
flowchart LR
    Start([Send<br/>Message]) --> PA[PROVIDER_ACCEPTANCE]
    
    PA --> D1{Sending<br/>Succeeds?}
    D1 -->|YES| SENT[SENT]
    D1 -->|NO| REJECTED[REJECTED]
    
    SENT --> D2{Delivery<br/>Succeeds?}
    D2 -->|YES| DELIVERED[DELIVERED]
    D2 -->|NO| UNDELIVERED[UNDELIVERED]
    
    DELIVERED --> D3{Read<br/>Receipt<br/>available?}
    D3 -->|YES| READ[READ]
    
    style PA fill:#87CEEB,stroke:#333,stroke-width:2px
    style SENT fill:#87CEEB,stroke:#333,stroke-width:2px
    style DELIVERED fill:#87CEEB,stroke:#333,stroke-width:2px
    style UNDELIVERED fill:#87CEEB,stroke:#333,stroke-width:2px
    style REJECTED fill:#87CEEB,stroke:#333,stroke-width:2px
    style READ fill:#87CEEB,stroke:#333,stroke-width:2px
```

**Delivery Confirmation Limitations**.

Status notifications are generally reliable indicators of message delivery. However, they depend on carrier-provided information and are not absolute guarantees. Factors affecting accuracy include:

- Carrier reporting delays
- Network issues
- Handset status (on/off, signal strength)


**RCS Channel Variants**

When sending via RCS, the flow varies based on availability of RCS and the message expiration:

RCS is unavailable when:

- Destination country lacks RCS coverage
- Destination operator doesn't support RCS
- Recipient's device doesn't support RCS


Alternative scenarios:

- RCS unavailable → automatic fallback to SMS
- RCS available but message expires → automatic fallback to SMS


In all these cases, the system automatically falls back to SMS and sends the message via that channel.


```mermaid
flowchart TD
    Start[Send RCS Message] --> Check{RCS Available?}
    Check -->|Yes| Send[Send via RCS]
    Check -->|No| Fallback1[Fallback to SMS]
    
    Send --> Expire{Message<br/>Expires?}
    Expire -->|No| Delivered[RCS Delivered]
    Expire -->|Yes| Fallback2[Fallback to SMS]
    
    Fallback1 --> SMS[Send via SMS]
    Fallback2 --> SMS
    
    style Start fill:#90EE90
    style Delivered fill:#90EE90
    style SMS fill:#87CEEB
```

Status Notifications:

- The message status webhook sends notifications for the SMS message
- In some cases, a `REJECTED` notification for the `RCS` channel may also be sent


| Scenario | RCS Status | SMS Status | Webhooks Sent |
|  --- | --- | --- | --- |
| RCS unavailable | - | DELIVERED | 1 (SMS only) |
| RCS expired | REJECTED | DELIVERED | 2 (RCS + SMS) |
| RCS delivered | DELIVERED | - | 1 (RCS only) |


When RCS expires and fallback occurs, you may receive two webhook notifications:

1. First: RCS channel with `REJECTED` status
2. Second: SMS channel with delivery status


Use the `channel` field to distinguish between them.

Example:


```
{
  "id": "87b81d7b-d0d8-4561-8caf-de0d14cbf9d1",
  ...
  "message": {
    "id": "3eb93593-4d81-4b9e-ba48-531f66cbcb74",
    "channel": "RCS",
    "status": "REJECTED"
  }
}
```