Otto Docs
Integrations

Webhook Integration

Otto exposes a generic webhook endpoint that allows external systems to trigger tasks. When a webhook is received, Otto queues it for asynchronous processing, creates a task, and hands it to the AI...

Otto exposes a generic webhook endpoint that allows external systems to trigger tasks. When a webhook is received, Otto queues it for asynchronous processing, creates a task, and hands it to the AI agent.


What Webhooks Enable

Webhooks let you connect Otto to external services without building custom integrations. When an event happens in an external system -- a GitHub push, a Stripe payment, an incoming SMS -- that system sends a webhook to Otto, and Otto creates a task to handle it.

The agent receives a description of the event and decides what to do. This makes webhooks a flexible way to automate responses to external triggers.


Endpoint

POST /webhook/{webhook_type}
ParameterDescription
webhook_typeA string identifying the source system (e.g., github, stripe, twilio, or any custom name)

The webhook type is used to generate task titles and descriptions tailored to each source.

Request Format

Send a JSON payload in the request body. Any valid JSON is accepted:

curl -X POST https://your-otto-backend.example.com/webhook/github \
  -H "Content-Type: application/json" \
  -d '{"action": "opened", "pull_request": {"title": "Add login page"}}'

If the body is not valid JSON, it is stored as a raw string under the raw_body key.

Response

{
  "status": "received",
  "message_id": "abc123-def456",
  "webhook_type": "github"
}

A 200 response means the webhook was queued for processing. Task creation and agent execution happen asynchronously.


Processing Pipeline

  1. Receive: The webhook endpoint accepts the request and captures the payload, headers, URL, and client IP.
  2. Queue: The webhook data is placed in an internal message queue for asynchronous processing.
  3. Process: A background worker reads from the queue, creates an Otto task with a generated title and description, and submits it to an ARQ worker for agent execution.
  4. Execute: The AI agent works on the task, using the webhook payload as context.
  5. Notify: When the task completes or fails, notifications are sent through the appropriate channel (Slack thread, DM, or web UI).

Supported Webhook Types

Otto has built-in handling for several webhook types. Each type generates tailored task titles and descriptions.

GitHub (github)

Otto parses GitHub webhook payloads to extract repository names, actions, senders, PR titles, and commit counts.

GitHub EventTask Description
Pull request opened"Process GitHub pull request opened by sender in repo: PR title"
Push"Process GitHub push by sender to repo (N commits)"
Other events"Process GitHub action event by sender in repository repo"

Example payload from GitHub:

{
  "action": "opened",
  "pull_request": {
    "title": "Add dark mode support",
    "body": "This PR adds dark mode theming to all components."
  },
  "repository": {
    "name": "otto-frontend"
  },
  "sender": {
    "login": "alice"
  }
}

Stripe (stripe)

Otto extracts the event type and event ID from Stripe webhook payloads.

FieldUsed For
typeEvent type (e.g., payment_intent.succeeded)
idEvent ID for tracking

Task description: "Process Stripe event_type event (ID: event_id)"

Slack (slack)

For generic Slack events routed through the webhook system (separate from Otto's native Slack integration). Extracts channel and user information from the event payload.

Twilio (twilio)

Otto extracts the message SID, sender number, and message body from Twilio webhook payloads.

FieldUsed For
FromSender phone number
BodySMS message content
MessageSidMessage tracking ID

Task description: "Process Twilio message from from_number (SID: message_sid)"

The agent receives the full SMS content and can respond accordingly.

Generic (any other type)

For any unrecognized webhook type, Otto creates a task with: "Process webhook_type webhook event with payload containing N fields."

Use any string as the webhook type to organize your integrations:

POST /webhook/jira
POST /webhook/pagerduty
POST /webhook/custom-crm

Monitoring

Health Check

GET /webhook/health

Returns the queue health status and statistics:

{
  "status": "healthy",
  "queue_stats": {
    "pending": 2,
    "processing": 1,
    "failed": 0
  },
  "service": "otto-webhook-ingestor"
}

Statistics

GET /webhook/stats

Returns queue statistics and recent failed messages for debugging:

{
  "queue_stats": {
    "pending": 0,
    "processing": 0,
    "failed": 1
  },
  "recent_failed_messages": [
    {
      "id": "msg-123",
      "error": "Failed to create task",
      "timestamp": "2026-02-19T10:30:00Z"
    }
  ]
}

Processing Status

GET /webhook/status

Returns whether the webhook background worker is running:

{
  "status": "running",
  "queue_stats": {
    "pending": 0,
    "processing": 0,
    "failed": 0
  }
}

Security Considerations

Slack Webhooks

Slack requests include a signing secret verification header. Otto verifies the HMAC SHA256 signature using SLACK_SIGNING_SECRET and rejects requests with invalid signatures or timestamps older than 5 minutes.

GitHub Webhooks

GitHub supports webhook secrets for payload signing. To verify GitHub webhooks, configure a secret in your GitHub webhook settings and implement signature verification on your side (e.g., via a reverse proxy or middleware). Otto's generic webhook endpoint does not perform GitHub-specific signature verification out of the box.

Stripe Webhooks

Stripe provides webhook signing secrets for verifying payload integrity. Similar to GitHub, you should verify Stripe signatures at the network edge or add verification middleware if needed.

General Recommendations

  • Place your Otto backend behind a reverse proxy (nginx, Cloudflare, etc.) and restrict webhook endpoint access by IP where possible.
  • Use HTTPS for all webhook URLs.
  • For sources that support it, configure webhook secrets and verify signatures.
  • Monitor the /webhook/stats endpoint for failed messages that could indicate unauthorized or malformed requests.

User Assignment

Webhook-created tasks are assigned to a system user (webhook@otto.local). This user is created automatically the first time a webhook is processed. If you need tasks assigned to specific users, the agent can reassign them based on the webhook payload content.


Troubleshooting

Webhooks are received but tasks are not created

  • Check /webhook/stats for failed messages and their error details.
  • Verify that REDIS_URL is set and Redis is reachable (the message queue depends on Redis).
  • Check backend logs for webhook processing errors.

Tasks are created but the agent does not run

  • Verify that ARQ workers are running. Tasks are submitted to the arq:interactive queue.
  • Check that DATABASE_URL and REDIS_URL are correctly configured.

External service is getting timeouts

  • The webhook endpoint returns immediately after queuing. If the endpoint is unreachable, check that the backend is running and the URL is correct.
  • Ensure port 8000 (or your configured port) is accessible from the external service.