> ## Documentation Index
> Fetch the complete documentation index at: https://docs.autoposting.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Real-time HTTP notifications for every event in your workspace — post published, clip rendered, agent run completed, token expired, and more.

Subscribe to Autoposting events and receive signed HTTP POST notifications at any URL you control. Use webhooks to trigger CMS updates, Slack alerts, database syncs, or downstream automation — without polling.

## Create a Webhook

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://app.autoposting.ai/api-proxy/webhooks \
    -H "Authorization: Bearer sk_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Production Pipeline",
      "url": "https://your-server.com/webhooks/autoposting",
      "events": ["post.published", "post.failed", "clip.render.completed"]
    }'
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch("https://app.autoposting.ai/api-proxy/webhooks", {
    method: "POST",
    headers: {
      Authorization: "Bearer sk_your_api_key",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "Production Pipeline",
      url: "https://your-server.com/webhooks/autoposting",
      events: ["post.published", "post.failed", "clip.render.completed"],
    }),
  });
  const { data } = await res.json();
  // data.webhook.secret — store immediately, shown only once
  ```
</CodeGroup>

<Warning>
  The webhook `secret` is returned **only at creation time**. Store it in a secrets manager immediately — if lost, delete the webhook and create a new one.
</Warning>

## Event Types

| Event                   | Trigger                                       |
| ----------------------- | --------------------------------------------- |
| `post.published`        | All targeted platforms succeeded              |
| `post.partial`          | Some platforms succeeded, at least one failed |
| `post.failed`           | All targeted platforms failed                 |
| `post.scheduled`        | Post saved with a future `scheduledAt`        |
| `post.cancelled`        | Scheduled post cancelled before publishing    |
| `clip.render.completed` | Render finished and uploaded                  |
| `clip.render.failed`    | Render failed — credits not consumed          |
| `agent.run.completed`   | Agent run finished successfully               |
| `agent.run.failed`      | Agent run failed                              |
| `token.expired`         | Connected platform token expired or revoked   |

## Delivery Format

Every delivery is a signed HTTP POST with this JSON envelope:

```json theme={null}
{
  "id": "evt_01HZ7XABCDEF1234567890",
  "event": "post.published",
  "timestamp": "2025-06-15T14:00:00Z",
  "orgId": "org_id",
  "data": {
    "postId": "post_id",
    "brandId": "brand_id",
    "platforms": ["x", "linkedin"],
    "publishedAt": "2025-06-15T14:00:03Z"
  }
}
```

Headers on every delivery: `X-Autoposting-Event`, `X-Autoposting-Delivery`, `X-Autoposting-Signature`, `X-Autoposting-Timestamp`.

## Verify Signatures

Autoposting signs every delivery using `HMAC-SHA256(secret, "${timestamp}.${rawBody}")`. Always verify before processing.

<CodeGroup>
  ```typescript Node.js theme={null}
  import { createHmac, timingSafeEqual } from "crypto";

  function verifyWebhook(payload: string, timestamp: string, signature: string, secret: string) {
    const expected = "sha256=" + createHmac("sha256", secret)
      .update(`${timestamp}.${payload}`)
      .digest("hex");
    return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  }

  app.post("/webhooks/autoposting", express.raw({ type: "application/json" }), (req, res) => {
    const valid = verifyWebhook(
      req.body.toString(),
      req.headers["x-autoposting-timestamp"] as string,
      req.headers["x-autoposting-signature"] as string,
      process.env.WEBHOOK_SECRET!
    );
    if (!valid) return res.status(401).send("Invalid signature");
    const event = JSON.parse(req.body.toString());
    res.status(200).send("OK");
  });
  ```

  ```python Python theme={null}
  import hashlib, hmac, os
  from flask import Flask, request, abort

  def verify_webhook(payload: bytes, timestamp: str, signature: str, secret: str) -> bool:
      expected = "sha256=" + hmac.new(
          secret.encode(), f"{timestamp}.{payload.decode()}".encode(), hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(signature, expected)

  @app.route("/webhooks/autoposting", methods=["POST"])
  def webhook():
      if not verify_webhook(request.data,
          request.headers.get("X-Autoposting-Timestamp", ""),
          request.headers.get("X-Autoposting-Signature", ""),
          os.environ["WEBHOOK_SECRET"]):
          abort(401)
      return "OK", 200
  ```
</CodeGroup>

<Warning>
  Always use timing-safe comparison (`timingSafeEqual` / `hmac.compare_digest`). Standard string equality is vulnerable to timing attacks. Reject deliveries where `X-Autoposting-Timestamp` is more than 5 minutes old to prevent replay attacks.
</Warning>

## Retry Behavior

Failed deliveries (non-2xx response, timeout, unreachable) are retried with exponential backoff: **\~5 min → 30 min → 2 h → 5 h → 10 h**.

Webhooks with repeated consecutive failures are auto-disabled. Re-enable after fixing the endpoint:

```bash theme={null}
curl -X PATCH https://app.autoposting.ai/api-proxy/webhooks/:id \
  -H "Authorization: Bearer sk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

## Idempotency

Deliveries may arrive more than once in rare cases. Use the `id` field in the payload as an idempotency key — store processed IDs and discard duplicates.

<CardGroup cols={2}>
  <Card title="Posts" icon="paper-plane" href="/concepts/posts">
    Subscribe to post lifecycle events — published, partial, failed.
  </Card>

  <Card title="Clips" icon="scissors" href="/concepts/clips">
    Get notified when clip renders complete or fail.
  </Card>

  <Card title="AI Agents" icon="robot" href="/concepts/agents">
    Receive agent run completion and failure events.
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/overview">
    Full endpoint reference for webhook management.
  </Card>
</CardGroup>
