This document serves as a comprehensive guide for understanding and using the webhook functionality in the messaging-sdk
. The webhook is designed to handle message delivery notifications from the API server, verifying the authenticity of the events and processing them as needed.
- Key Concepts
- Setting Up the Webhook
- Validating the Signature
- How to Use
- Testing the Webhook
- Advanced Features
- Additional Resources
When a message is sent, it is initially marked as queued
. The API server simulates a sending queue and eventually updates the status to delivered
or failed
. This status update is communicated to your application through a webhook URL, configured as:
http://localhost:3010/webhooks
Each webhook event includes an Authorization
header containing an HMAC signature. The signature is generated using the payload and a secret key. Your application must validate the signature to ensure the event is authentic.
Ensure the environment variables are set in your .env
file. Copy all from .env.example
and paste them to newly created .env
, and update the values accordingly.
The webhook server is implemented using FastAPI
. To start the server:
uvicorn src.server.app:app --reload --port 3010
The server will run at http://localhost:3010
.
The SDK provides a verify_signature
utility to validate the HMAC signature. Here's how it works:
- The server extracts the raw payload and the
Authorization
header. - The
verify_signature
function compares the provided signature with a locally generated one. - If the signatures match, the payload is processed.
Example Code:
from src.core.security import verify_signature
try:
verify_signature(message=raw_payload, signature=auth_header, secret=WEBHOOK_SECRET)
print("Signature validated successfully.")
except ValueError:
print("Invalid signature.")
The endpoint /webhooks
is used to receive events. Here’s an example payload:
{
"id": "msg123",
"status": "delivered",
"deliveredAt": "2024-12-01T12:00:00Z"
}
The server processes payloads as follows:
-
Signature Validation:
- Verifies the
Authorization
header using theWEBHOOK_SECRET
.
- Verifies the
-
Payload Parsing:
- Converts the raw request body into a structured object using the
WebhookPayload
schema.
- Converts the raw request body into a structured object using the
-
Event Handling:
- Logs the payload and prints it to the console (as required by the assignment).
Example of processing in app.py
:
@app.post("/webhooks")
async def handle_webhook(
payload: WebhookPayload,
authorization: str = Header(...),
request: Request
):
try:
raw_body = await request.body()
verify_signature(raw_body, authorization, settings.WEBHOOK_SECRET)
logger.info(f"Webhook received: {payload.model_dump()}")
print(f"Processed webhook payload: {payload.model_dump()}")
return {"message": "Webhook processed successfully."}
except ValueError:
raise HTTPException(status_code=401, detail="Unauthorized")
except Exception:
raise HTTPException(status_code=500, detail="Internal Server Error")
Test the webhook by sending a simulated payload to it:
curl -X POST http://localhost:3010/webhooks \
-H "Authorization: Bearer <calculated-signature>" \
-d '{"id": "msg123", "status": "delivered", "deliveredAt": "2024-11-30T12:00:00Z"}'
For example,
secret="mySecret"
payload='{"id": "msg123", "status": "delivered", "deliveredAt": "2024-11-30T12:00:00Z"}'
signature=$(echo -n $payload | openssl dgst -sha256 -hmac $secret | awk '{print $2}')
curl -X POST http://localhost:3010/webhooks \
-H "Authorization: Bearer $signature" \
-H "Content-Type: application/json" \
-d "$payload"
The response should be look like this,
{"message":"Webhook processed successfully."}
Run unit tests to verify webhook functionality:
pytest tests/unit/test_server
Run integration tests to verify webhook functionality:
pytest tests/integration/test_webhook.py
- Cause: Invalid signature in the
Authorization
header. - Solution: Ensure the
WEBHOOK_SECRET
is correct and the payload is serialized properly.
- Cause: Invalid payload structure.
- Solution: Validate the payload against the
WebhookPayload
schema.
The webhook server is modular and can be extended for advanced use cases:
- Custom Routes: Add new endpoints for other event types.
- Database Integration: Store event payloads in a database for future analysis.
- Retry Mechanism: Implement logic to retry failed webhooks.