Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/jonwiggins/optio/llms.txt

Use this file to discover all available pages before exploring further.

Optio can send webhook events to any URL when a task transitions to a new state. Use webhooks to trigger downstream automation — post to Slack, update a dashboard, kick off a deployment pipeline, or anything else your workflow requires.

Setting up a webhook

1

Open webhook settings

Go to Settings → Webhooks and click Add webhook.
2

Enter the endpoint URL

Provide the HTTPS URL that should receive the events. Optio will POST JSON to this address.
3

Choose events

Select one or more event types to subscribe to. You can change this later.
4

Add a signing secret (recommended)

Enter a random string as the Secret. Optio will include an X-Optio-Signature header on every delivery so you can verify the payload was not tampered with. Keep this value private.
5

Save

Click Save. Optio will start delivering events immediately.

Event types

Fired when a task reaches the completed state. This typically happens when the pull request opened by the agent is merged.When to use: trigger post-merge actions such as deploying to staging, posting a release note, or closing a related ticket in an external system.
Fired when a task reaches the failed state. This can happen if the agent exits with an error or if the PR is closed without merging.When to use: alert your team via Slack or PagerDuty, or open an incident ticket automatically.
Fired when a task reaches the needs_attention state. This occurs when a reviewer has requested changes and auto-resume is not enabled, or when the agent encounters an issue it cannot resolve autonomously.When to use: notify a human that input is needed before the agent can continue.
Fired when the agent opens a pull request and the task transitions to pr_opened.When to use: post a link to the PR in a team chat channel, or trigger a custom CI step.
Fired when a code review subtask completes. The review agent may approve the PR or request changes.When to use: track review completion separately from task completion, or trigger custom logic based on review outcomes.

Webhook payload

Every event is delivered as a POST request with a JSON body. The payload structure is:
{
  "event": "task.completed",
  "taskId": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
  "task": {
    "id": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
    "title": "Fix login redirect after checkout",
    "state": "completed",
    "repoUrl": "https://github.com/acme/backend",
    "prUrl": "https://github.com/acme/backend/pull/142",
    "prNumber": 142,
    "agentType": "claude-code",
    "costUsd": "0.84",
    "createdAt": "2025-03-26T10:00:00Z",
    "completedAt": "2025-03-26T10:12:34Z"
  },
  "timestamp": "2025-03-26T10:12:34Z"
}

Delivery and retries

Optio delivers each event asynchronously via a background worker. If your endpoint returns a non-2xx status code or does not respond within the timeout window, Optio will retry the delivery with exponential backoff. You can inspect recent deliveries — including the HTTP status code returned by your endpoint and the full payload — in the Delivery log for each webhook: go to Settings → Webhooks → [webhook] → Deliveries.

Verifying webhook signatures

When you set a secret on a webhook, Optio signs every payload with HMAC-SHA256 and includes the signature in the X-Optio-Signature header as sha256=<hex-digest>. Verify the signature in your endpoint before processing the payload:
import { createHmac, timingSafeEqual } from "node:crypto";

function verifySignature(rawBody, signatureHeader, secret) {
  const expected = "sha256=" + createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  const sig = Buffer.from(signatureHeader);
  const exp = Buffer.from(expected);

  if (sig.length !== exp.length) return false;
  return timingSafeEqual(sig, exp);
}

// Express example
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-optio-signature"];
  if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }
  const payload = JSON.parse(req.body.toString());
  // process payload...
  res.sendStatus(200);
});
Always use a constant-time comparison function (such as timingSafeEqual in Node.js or hmac.compare_digest in Python) when comparing signatures. Standard equality checks are vulnerable to timing attacks.

Deleting a webhook

Go to Settings → Webhooks, find the webhook you want to remove, and click Delete. Pending deliveries in the queue will not be sent after deletion.

Build docs developers (and LLMs) love