Outbound Webhooks#
Subscribe to Outeract events and receive them at your endpoint in real-time.
Overview#
Outbound webhooks allow you to:
- Receive messages as they arrive
- Track message delivery status
- React to custom events
- Build real-time integrations
Creating a Subscription#
Webhook subscriptions are managed in the console under Settings → Webhooks. Each subscription has:
| Field | Description |
|---|---|
| Name | A label for the subscription, e.g. Message Handler |
| URL | Your HTTPS endpoint, e.g. https://myapp.com/webhooks/outeract |
| Event types | Patterns to match, e.g. message.inbound, message.* (see below) |
| Secret | Used to sign deliveries. Leave blank and one is generated for you |
| Enabled | Toggle delivery off without deleting the subscription |
Event Patterns#
Event patterns determine which events trigger your webhook.
Exact Match#
["message.inbound", "message.outbound"]Only matches these exact event types.
Wildcard Patterns#
["message.*"]Matches any event starting with message.:
message.inboundmessage.outboundmessage.deliveredmessage.read
All Events#
["*"]Matches all events (use with caution).
Common Patterns#
| Pattern | Matches |
|---|---|
message.* | All message events |
message.inbound | Incoming messages only |
message.outbound | Outgoing messages only |
message.status | Delivery status updates |
custom.* | All custom events |
user.* | User-related events |
link_code.* | Link code events |
Webhook Payload#
Structure#
{
"event_id": "123e4567-e89b-12d3-a456-426614174000",
"event_type": "message.inbound",
"timestamp": "2024-01-15T10:30:00Z",
"app_id": "your-app-uuid",
"data": {
"type": "message",
"message": {
"text": "Hello!",
"role": "user"
},
"user_id": "sender-user-uuid",
"from_user_id": "sender-user-uuid",
"from_identity_id": "sender-identity-uuid",
"to_user_id": "recipient-user-uuid",
"to_identity_id": "recipient-identity-uuid"
},
"edges": [
{
"edge_type": "sent_by",
"target_node_type": "identity",
"target_node_id": "sender-identity-uuid"
},
{
"edge_type": "sent_to",
"target_node_type": "identity",
"target_node_id": "recipient-identity-uuid"
}
]
}Fields#
| Field | Type | Description |
|---|---|---|
event_id | UUID | Source event ID |
event_type | string | Event type (e.g., message.inbound) |
timestamp | datetime | When the event was created |
app_id | UUID | Your application ID |
data | object | Event payload, enriched with sender/recipient user and identity IDs for message events |
edges | array | Graph edges from the event to related entities (sender, recipient, files) |
Verifying Webhooks#
Every webhook includes a signature for verification.
Headers#
X-Outeract-Signature: sha256=abc123...
X-Outeract-Event-Type: message.inbound
X-Outeract-Event-ID: 123e4567-e89b-12d3-a456-426614174000
X-Outeract-Delivery-ID: delivery-uuid
X-Outeract-Timestamp: 2024-01-15T10:30:00+00:00The signature is an HMAC-SHA256 of the raw request body, keyed with your subscription secret.
Verification (Python)#
import hmac
import hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(),
body,
hashlib.sha256
).hexdigest()
# Constant-time comparison
return hmac.compare_digest(expected, signature)Verification (Node.js)#
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
// Constant-time comparison
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}Responding to Webhooks#
Success Response#
Return HTTP 200-299 within 30 seconds:
{
"received": true
}Or:
HTTP/1.1 200 OKError Response#
If processing fails, return 4xx or 5xx:
HTTP/1.1 500 Internal Server Error
{
"error": "Processing failed"
}The webhook will be retried according to the retry policy.
Retry Policy#
Failed deliveries are retried with exponential backoff, up to the subscription’s max attempts (default: 5). Once the maximum is reached, the delivery is marked as failed. Every attempt is recorded in the delivery history.
Managing Subscriptions#
All subscription management happens in the console under Settings → Webhooks:
| Action | Where |
|---|---|
| List | The Webhooks page shows every subscription with its URL, event types and enabled state |
| Update | Open a subscription to change its name, URL, event types, headers or retry settings |
| Pause | Toggle Enabled off; the subscription is kept but deliveries stop |
| Delete | Remove the subscription permanently from its detail page |
Delivery Logs#
The subscription detail page lists delivery history: status, HTTP status, attempt count, error message and timestamp for each attempt.
Status Values#
| Status | Description |
|---|---|
pending | Queued for delivery |
delivered | Successfully delivered |
failed | All retries exhausted |
retrying | Failed, will retry |
Testing Webhooks#
Test Endpoint#
The subscription’s test action in the console sends a webhook.test event to your endpoint and reports whether it succeeded, along with the HTTP status and response body it got back.
Webhook Debugging Tools#
- webhook.site - Inspect payloads online
- ngrok - Expose local server
- RequestBin - Collect and inspect requests
Local Development#
# Start ngrok
ngrok http 3000
# Use the ngrok URL as your webhook endpoint
# https://abc123.ngrok.io/webhooks/outeractBest Practices#
1. Return Fast#
Process webhooks asynchronously:
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
@app.post("/webhooks/outeract")
async def webhook(request: Request, background_tasks: BackgroundTasks):
body = await request.body()
# Verify signature
if not verify_webhook(body, request.headers):
return JSONResponse(status_code=401)
# Queue for async processing
background_tasks.add_task(process_event, json.loads(body))
return {"received": True}2. Handle Duplicates#
Use event_id for idempotency:
processed_events = set() # Use Redis/DB in production
async def process_event(data):
event_id = data["event_id"]
if event_id in processed_events:
return # Already processed
processed_events.add(event_id)
# Process event...3. Secure Your Endpoint#
- Always verify signatures
- Use HTTPS
- Validate timestamp to prevent replay attacks
- Keep your secret secure
4. Monitor and Alert#
- Track webhook delivery success rate
- Alert on high failure rates
- Monitor response times
5. Use Meaningful Patterns#
Subscribe only to events you need:
// Good - specific patterns
["message.inbound", "message.status"]
// Avoid - too broad
["*"]Event Types Reference#
Message Events#
| Event Type | Description |
|---|---|
message.inbound | Incoming message received (includes reactions) |
message.outbound | Outgoing message sent |
System Events#
| Event Type | Description |
|---|---|
system.message_delivery_failed | Message delivery failed (e.g., 24-hour window expired) |
Link Code Events#
| Event Type | Description |
|---|---|
link_code.generated | Link code created |
link_code.activation | Link code used |
User Events#
| Event Type | Description |
|---|---|
user.merged | Users merged |
Custom Events#
Your custom event types:
custom.order.created
custom.payment.completed
custom.support.ticket.opened