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:

FieldDescription
NameA label for the subscription, e.g. Message Handler
URLYour HTTPS endpoint, e.g. https://myapp.com/webhooks/outeract
Event typesPatterns to match, e.g. message.inbound, message.* (see below)
SecretUsed to sign deliveries. Leave blank and one is generated for you
EnabledToggle 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.inbound
  • message.outbound
  • message.delivered
  • message.read

All Events#

["*"]

Matches all events (use with caution).

Common Patterns#

PatternMatches
message.*All message events
message.inboundIncoming messages only
message.outboundOutgoing messages only
message.statusDelivery 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#

FieldTypeDescription
event_idUUIDSource event ID
event_typestringEvent type (e.g., message.inbound)
timestampdatetimeWhen the event was created
app_idUUIDYour application ID
dataobjectEvent payload, enriched with sender/recipient user and identity IDs for message events
edgesarrayGraph 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:00

The 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 OK

Error 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:

ActionWhere
ListThe Webhooks page shows every subscription with its URL, event types and enabled state
UpdateOpen a subscription to change its name, URL, event types, headers or retry settings
PauseToggle Enabled off; the subscription is kept but deliveries stop
DeleteRemove 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#

StatusDescription
pendingQueued for delivery
deliveredSuccessfully delivered
failedAll retries exhausted
retryingFailed, 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#

  1. webhook.site - Inspect payloads online
  2. ngrok - Expose local server
  3. 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/outeract

Best 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 TypeDescription
message.inboundIncoming message received (includes reactions)
message.outboundOutgoing message sent

System Events#

Event TypeDescription
system.message_delivery_failedMessage delivery failed (e.g., 24-hour window expired)
Event TypeDescription
link_code.generatedLink code created
link_code.activationLink code used

User Events#

Event TypeDescription
user.mergedUsers merged

Custom Events#

Your custom event types:

custom.order.created
custom.payment.completed
custom.support.ticket.opened