Skip to content

Commit b153152

Browse files
authored
DEVEXP-1310: Redesign Webhooks - SMS (#132)
1 parent c812821 commit b153152

36 files changed

Lines changed: 231 additions & 195 deletions

examples/sinch_events/.env.example

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Server Configuration
22
SERVER_PORT =
33

4-
# Webhook Configuration
5-
# The secret value used for webhook calls validation
4+
# Sinch Event Configuration
5+
# The secret value used for Sinch Event callback validation
66
# See https://developers.sinch.com/docs/numbers/api-reference/numbers/tag/Numbers-Callbacks/
7-
NUMBERS_WEBHOOKS_SECRET = NUMBERS_WEBHOOKS_SECRET
7+
NUMBERS_SINCH_EVENT_SECRET = NUMBERS_SINCH_EVENT_SECRET
88
# See https://developers.sinch.com/docs/sms/api-reference/sms/tag/Webhooks/#tag/Webhooks/section/Callbacks
9-
SMS_WEBHOOKS_SECRET = SMS_WEBHOOKS_SECRET
9+
SMS_SINCH_EVENT_SECRET = SMS_SINCH_EVENT_SECRET
1010
# See https://developers.sinch.com/docs/conversation/callbacks
11-
CONVERSATION_WEBHOOKS_SECRET = CONVERSATION_WEBHOOKS_SECRET
11+
CONVERSATION_SINCH_EVENT_SECRET = CONVERSATION_SINCH_EVENT_SECRET

examples/sinch_events/README.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
This directory contains a server application built with [Sinch Python SDK](https://github.com/sinch/sinch-sdk-python)
44
to process incoming webhooks from Sinch services.
55

6-
The webhook handlers are organized by service:
7-
- **SMS**: Handlers for SMS webhook events (`sms_api/`)
8-
- **Numbers**: Handlers for Numbers API webhook events (`numbers_api/`)
9-
- **Conversation**: Handlers for Conversation API webhook events (`conversation_api/`)
6+
The Sinch Events Handlers are organized by service:
7+
- **SMS**: Handlers for SMS events (`sms_api/`)
8+
- **Numbers**: Handlers for Numbers API events (`numbers_api/`)
9+
- **Conversation**: Handlers for Conversation API events (`conversation_api/`)
1010

11-
This directory contains both the webhook handlers and the server application (`server.py`) that uses them.
11+
This directory contains both the Event handlers and the server application (`server.py`) that uses them.
1212

1313
## Requirements
1414

@@ -32,17 +32,17 @@ This directory contains both the webhook handlers and the server application (`s
3232
- Controller Settings
3333
- Numbers controller: Set the `numbers` Sinch Event secret. You can retrieve it using the `/event_destination` endpoint (see SDK implementation: [event_destinations_apis.py](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/event_destinations_apis.py); for additional details, refer to the [Numbers API callbacks documentation](https://developers.sinch.com/docs/numbers/api-reference/numbers/tag/Numbers-Callbacks/)):
3434
```
35-
NUMBERS_WEBHOOKS_SECRET=Your Sinch Numbers Webhook Secret
35+
NUMBERS_SINCH_EVENT_SECRET=Your Sinch Numbers Sinch Event Secret
3636
```
37-
- SMS controller: To configure the `sms` webhooks secret, contact your account manager to enable authentication for SMS callbacks. For more details, refer to
37+
- SMS controller: To configure the `sms` Sinch Event secret, contact your account manager to enable authentication for SMS callbacks. For more details, refer to
3838
[SMS API](https://developers.sinch.com/docs/sms/api-reference/sms/tag/Webhooks/#tag/Webhooks/section/Callbacks),
3939

4040
```
41-
SMS_WEBHOOKS_SECRET=Your Sinch SMS Webhook Secret
41+
SMS_SINCH_EVENT_SECRET=Your Sinch SMS Sinch Event Secret
4242
```
4343
- Conversation controller: Set the webhook secret you configured when creating the webhook (see [Conversation API callbacks](https://developers.sinch.com/docs/conversation/callbacks)):
4444
```
45-
CONVERSATION_WEBHOOKS_SECRET=Your Conversation Webhook Secret
45+
CONVERSATION_SINCH_EVENT_SECRET=Your Conversation Sinch Event Secret
4646
```
4747

4848
## Usage
@@ -96,18 +96,18 @@ ngrok
9696
...
9797
Forwarding https://adbd-79-148-170-158.ngrok-free.app -> http://localhost:3001
9898
```
99-
Use the `https` forwarding URL in your callback configuration. For example:
99+
Use the `https` forwarding URL in your event destination configuration. For example:
100100
- Numbers: https://adbd-79-148-170-158.ngrok-free.app/NumbersEvent
101101
- SMS: https://adbd-79-148-170-158.ngrok-free.app/SmsEvent
102102
- Conversation: https://adbd-79-148-170-158.ngrok-free.app/ConversationEvent
103103

104-
Use this value to configure the callback URLs:
105-
- **Numbers**: Set the `callback_url` parameter when renting or updating a number via the SDK (e.g., `available_numbers_apis` rent/update flow: [rent](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/available_numbers_apis.py#L69), [update](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/available_numbers_apis.py#L89)); you can also update active numbers via `active_numbers_apis` ([example](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/active_numbers_apis.py#L64)).
106-
- **SMS**: Set the `callback_url` parameter when configuring your SMS service plan via the SDK (see `batches_apis` examples: [send/dry-run callbacks](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/sms/api/v1/batches_apis.py#L146), [update/replace callbacks](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/sms/api/v1/batches_apis.py#L491)); you can also set it directly via the SMS API.
104+
Use this value to configure the Sinch Events URLs:
105+
- **Numbers**: Set the `event_destination_target` parameter when renting or updating a number via the SDK (e.g., `available_numbers_apis` rent/update flow: [rent](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/available_numbers_apis.py#L69), [update](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/available_numbers_apis.py#L89)); you can also update active numbers via `active_numbers_apis` ([example](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/active_numbers_apis.py#L64)).
106+
- **SMS**: Set the `event_destination_target` parameter when configuring your SMS service plan via the SDK (see `batches_apis` examples: [send/dry-run callbacks](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/sms/api/v1/batches_apis.py#L146), [update/replace callbacks](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/sms/api/v1/batches_apis.py#L491)); you can also set it directly via the SMS API.
107107
- **Conversation**: Set the `callback_url` parameter when sending a message via the SDK (see `messages_apis` example: [send_text_message](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/conversation/api/v1/messages_apis.py#L420)).
108108

109-
You can also set these callback URLs in the Sinch dashboard; the API parameters above override the default values configured there.
109+
You can also set these Sinch Events URLs in the Sinch dashboard; the API parameters above override the default values configured there.
110110

111-
> **Note**: If you have set a webhook secret (e.g., `SMS_WEBHOOKS_SECRET`), the webhook URL must be configured in the Sinch dashboard
112-
> and cannot be overridden via API parameters. The webhook secret is used to validate incoming webhook requests,
111+
> **Note**: If you have set a Sinch Event secret (e.g., `SMS_SINCH_EVENT_SECRET`), the Sinch Event URL must be configured in the Sinch dashboard
112+
> and cannot be overridden via API parameters. The Sinch Event secret is used to validate incoming webhook requests,
113113
> and the URL associated with it must be set in the dashboard.

examples/sinch_events/conversation_api/controller.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
from flask import request, Response
2-
from webhooks.conversation_api.server_business_logic import handle_conversation_event
2+
from sinch_events.conversation_api.server_business_logic import handle_conversation_event
33

44

55
class ConversationController:
6-
def __init__(self, sinch_client, webhooks_secret):
6+
def __init__(self, sinch_client, sinch_event_secret):
77
self.sinch_client = sinch_client
8-
self.webhooks_secret = webhooks_secret
8+
self.sinch_event_secret = sinch_event_secret
99
self.logger = self.sinch_client.configuration.logger
1010

1111
def conversation_event(self):
1212
headers = dict(request.headers)
1313
raw_body = request.raw_body if request.raw_body else b""
1414

15-
webhooks_service = self.sinch_client.conversation.webhooks(self.webhooks_secret)
15+
webhooks_service = self.sinch_client.conversation.webhooks(
16+
self.sinch_event_secret
17+
)
1618

1719
# Set to True to enforce signature validation (recommended in production)
1820
ensure_valid_signature = False

examples/sinch_events/numbers_api/controller.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
from flask import request, Response
2-
from webhooks.numbers_api.server_business_logic import handle_numbers_event
2+
from sinch_events.numbers_api.server_business_logic import handle_numbers_event
33

44

55
class NumbersController:
6-
def __init__(self, sinch_client, webhooks_secret):
6+
def __init__(self, sinch_client, sinch_event_secret):
77
self.sinch_client = sinch_client
8-
self.webhooks_secret = webhooks_secret
8+
self.sinch_event_secret = sinch_event_secret
99
self.logger = self.sinch_client.configuration.logger
1010

1111
def numbers_event(self):
1212
headers = dict(request.headers)
1313
raw_body = request.raw_body if request.raw_body else b""
1414

1515
sinch_events_service = self.sinch_client.numbers.sinch_events(
16-
self.webhooks_secret
16+
self.sinch_event_secret
1717
)
1818

1919
ensure_valid_authentication = False

examples/sinch_events/server.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,35 @@
22
import sys
33
from pathlib import Path
44

5-
# Add examples directory to Python path to allow importing webhooks
5+
# Add examples directory to Python path to allow importing sinch_events
66
examples_dir = Path(__file__).resolve().parent.parent
77
if str(examples_dir) not in sys.path:
88
sys.path.insert(0, str(examples_dir))
99

1010
from flask import Flask, request
11-
from webhooks.numbers_api.controller import NumbersController
12-
from webhooks.sms_api.controller import SmsController
13-
from webhooks.conversation_api.controller import ConversationController
14-
from webhooks.sinch_client_helper import get_sinch_client, load_config
11+
from sinch_events.numbers_api.controller import NumbersController
12+
from sinch_events.sms_api.controller import SmsController
13+
from sinch_events.conversation_api.controller import ConversationController
14+
from sinch_events.sinch_client_helper import get_sinch_client, load_config
1515

1616
app = Flask(__name__)
1717

1818
config = load_config()
1919
port = int(config.get('SERVER_PORT') or 3001)
20-
numbers_webhooks_secret = config.get('NUMBERS_WEBHOOKS_SECRET')
21-
sms_webhooks_secret = config.get('SMS_WEBHOOKS_SECRET')
22-
conversation_webhooks_secret = config.get('CONVERSATION_WEBHOOKS_SECRET')
20+
numbers_sinch_event_secret = config.get('NUMBERS_SINCH_EVENT_SECRET')
21+
sms_sinch_event_secret = config.get('SMS_SINCH_EVENT_SECRET')
22+
conversation_sinch_event_secret = config.get('CONVERSATION_SINCH_EVENT_SECRET')
2323
sinch_client = get_sinch_client(config)
2424

2525
# Set up logging at the INFO level
2626
logging.basicConfig()
2727
sinch_client.configuration.logger.setLevel(logging.INFO)
2828

29-
numbers_controller = NumbersController(sinch_client, numbers_webhooks_secret)
30-
sms_controller = SmsController(sinch_client, sms_webhooks_secret)
31-
conversation_controller = ConversationController(sinch_client, conversation_webhooks_secret or '')
29+
numbers_controller = NumbersController(sinch_client, numbers_sinch_event_secret)
30+
sms_controller = SmsController(sinch_client, sms_sinch_event_secret)
31+
conversation_controller = ConversationController(
32+
sinch_client, conversation_sinch_event_secret or ''
33+
)
3234

3335

3436
# Middleware to capture raw body

examples/sinch_events/sinch_client_helper.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
def load_config() -> dict[str, str]:
77
"""
8-
Load configuration from the .env file in the webhooks directory.
8+
Load configuration from the .env file in the sinch_events directory.
99
1010
Returns:
1111
dict[str, str]: Dictionary containing configuration values
@@ -15,7 +15,7 @@ def load_config() -> dict[str, str]:
1515
env_file = current_dir / '.env'
1616

1717
if not env_file.exists():
18-
raise FileNotFoundError(f"Could not find .env file in webhooks directory: {env_file}")
18+
raise FileNotFoundError(f"Could not find .env file in sinch_events directory: {env_file}")
1919

2020
config_dict = dotenv_values(env_file)
2121

examples/sinch_events/sms_api/controller.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
from flask import request, Response
2-
from webhooks.sms_api.server_business_logic import (
2+
from sinch_events.sms_api.server_business_logic import (
33
handle_sms_event,
44
)
55

66

77
class SmsController:
8-
def __init__(self, sinch_client, webhooks_secret):
8+
def __init__(self, sinch_client, sinch_event_secret):
99
self.sinch_client = sinch_client
10-
self.webhooks_secret = webhooks_secret
10+
self.sinch_event_secret = sinch_event_secret
1111
self.logger = self.sinch_client.configuration.logger
1212

1313
def sms_event(self):
1414
headers = dict(request.headers)
1515
raw_body = request.raw_body if request.raw_body else b""
1616

17-
webhooks_service = self.sinch_client.sms.webhooks(self.webhooks_secret)
17+
sinch_events_service = self.sinch_client.sms.sinch_events(self.sinch_event_secret)
1818

1919
# Signature headers may be absent unless your account manager enables them
2020
# (see README: Configuration -> Controller Settings -> SMS controller);
2121
# leave auth disabled here unless SMS callbacks are configured.
2222
ensure_valid_authentication = False
2323
if ensure_valid_authentication:
24-
valid_auth = webhooks_service.validate_authentication_header(
24+
valid_auth = sinch_events_service.validate_authentication_header(
2525
headers=headers,
2626
json_payload=raw_body,
2727
)
2828

2929
if not valid_auth:
3030
return Response(status=401)
3131

32-
event = webhooks_service.parse_event(raw_body, headers)
32+
event = sinch_events_service.parse_event(raw_body, headers)
3333

3434
handle_sms_event(sms_event=event, logger=self.logger)
3535

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
from sinch.domains.sms.webhooks.v1.events.sms_webhooks_event import IncomingSMSWebhookEvent
1+
from sinch.domains.sms.sinch_events.v1.events.sms_sinch_event import (
2+
IncomingSMSSinchEvent,
3+
)
24

35

4-
def handle_sms_event(sms_event: IncomingSMSWebhookEvent, logger):
6+
def handle_sms_event(sms_event: IncomingSMSSinchEvent, logger):
57
"""
68
This method handles an SMS event.
79
Args:
8-
sms_event (SmsWebhooksEvent): The SMS event data.
10+
sms_event (IncomingSMSSinchEvent): The SMS event data.
911
logger (logging.Logger, optional): Logger instance for logging. Defaults to None.
1012
"""
1113
logger.info(f'Handling SMS event:\n{sms_event.model_dump_json(indent=2)}')

0 commit comments

Comments
 (0)