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

# Swytcho Webhooks: Receive Real-Time Event Notifications

> Configure Swytcho webhooks to receive real-time event notifications on your server, then verify HMAC signatures to process deliveries safely.

Webhooks let Swytcho push event notifications directly to your server the moment something happens — a request completes, a request fails, or your usage crosses a threshold. Instead of polling the API repeatedly, your application receives a `POST` request with a structured payload and can react immediately.

## Supported Event Types

| Event                     | Description                                                            |
| ------------------------- | ---------------------------------------------------------------------- |
| `request.completed`       | A model request finished successfully and a response was returned.     |
| `request.failed`          | A model request failed after all internal retries were exhausted.      |
| `usage.threshold_reached` | Your account's usage crossed a configured spending or token threshold. |

## Register a Webhook Endpoint

<Tabs>
  <Tab title="Dashboard">
    <Steps>
      <Step title="Open Webhook Settings">
        In the Swytcho dashboard, navigate to **Settings → Webhooks**.
      </Step>

      <Step title="Add an Endpoint">
        Click **Add Endpoint**, enter the HTTPS URL your server will listen on, and select the event types you want to receive.
      </Step>

      <Step title="Save and Copy the Secret">
        Click **Save**. Swytcho generates a signing secret for the endpoint — copy it now and store it securely. You'll use it to verify incoming webhook signatures.
      </Step>
    </Steps>
  </Tab>

  <Tab title="API">
    Send a `POST` request to `/v1/webhooks` with your endpoint URL and the list of events to subscribe to:

    ```bash cURL theme={null}
    curl -X POST https://api.swytcho.com/v1/webhooks \
      -H "Authorization: Bearer YOUR_SWYTCHO_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://your-server.example.com/webhooks/swytcho",
        "events": ["request.completed", "request.failed", "usage.threshold_reached"]
      }'
    ```

    The response includes a `secret` field. Store this value — it cannot be retrieved again.
  </Tab>
</Tabs>

## Webhook Payload

Swytcho sends an HTTP `POST` to your endpoint with a JSON body and a `Swytcho-Signature` header. The payload structure looks like this:

```json JSON theme={null}
{
  "id": "evt_01j9x2k5m3n8p7q6r4s0t",
  "type": "request.completed",
  "created_at": "2024-11-15T10:34:22Z",
  "data": {
    "request_id": "req_01j9x2k5m3n8p7q6r4s0t",
    "model": "swytcho-1",
    "prompt_tokens": 128,
    "completion_tokens": 256,
    "total_tokens": 384,
    "latency_ms": 843,
    "status": "completed"
  }
}
```

The top-level `type` field always matches one of the [supported event types](#supported-event-types) above.

## Verify Webhook Signatures

<Warning>
  Always verify the `Swytcho-Signature` header before processing a webhook payload. Skipping verification means your endpoint will accept forged requests from anyone who knows its URL.
</Warning>

Swytcho signs every webhook delivery using HMAC-SHA256 with your endpoint's signing secret. The `Swytcho-Signature` header contains a timestamp and one or more signatures in the format `t=<timestamp>,v1=<signature>`.

<CodeGroup>
  ```python Python theme={null}
  import hashlib
  import hmac
  import time

  def verify_webhook(payload_body: bytes, signature_header: str, secret: str) -> bool:
      """
      Verify a Swytcho webhook signature.

      :param payload_body: The raw request body as bytes.
      :param signature_header: The value of the Swytcho-Signature header.
      :param secret: Your endpoint's signing secret.
      :raises ValueError: If the signature is invalid or the timestamp is stale.
      """
      parts = dict(part.split("=", 1) for part in signature_header.split(","))
      timestamp = parts.get("t")
      received_sig = parts.get("v1")

      if not timestamp or not received_sig:
          raise ValueError("Malformed Swytcho-Signature header.")

      # Reject payloads older than 5 minutes to prevent replay attacks
      if abs(time.time() - int(timestamp)) > 300:
          raise ValueError("Webhook timestamp is too old.")

      signed_payload = f"{timestamp}.".encode() + payload_body
      expected_sig = hmac.new(
          secret.encode(), signed_payload, hashlib.sha256
      ).hexdigest()

      if not hmac.compare_digest(expected_sig, received_sig):
          raise ValueError("Webhook signature verification failed.")

      return True
  ```

  ```javascript Node.js theme={null}
  import crypto from "crypto";

  /**
   * Verify a Swytcho webhook signature.
   * @param {Buffer} payloadBody - The raw request body.
   * @param {string} signatureHeader - The value of the Swytcho-Signature header.
   * @param {string} secret - Your endpoint's signing secret.
   */
  function verifyWebhook(payloadBody, signatureHeader, secret) {
    const parts = Object.fromEntries(
      signatureHeader.split(",").map((p) => p.split("="))
    );

    const timestamp = parts["t"];
    const receivedSig = parts["v1"];

    if (!timestamp || !receivedSig) {
      throw new Error("Malformed Swytcho-Signature header.");
    }

    // Reject payloads older than 5 minutes to prevent replay attacks
    if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 300) {
      throw new Error("Webhook timestamp is too old.");
    }

    const signedPayload = Buffer.concat([
      Buffer.from(`${timestamp}.`),
      payloadBody,
    ]);

    const expectedSig = crypto
      .createHmac("sha256", secret)
      .update(signedPayload)
      .digest("hex");

    if (!crypto.timingSafeEqual(Buffer.from(expectedSig), Buffer.from(receivedSig))) {
      throw new Error("Webhook signature verification failed.");
    }

    return true;
  }
  ```
</CodeGroup>

## Respond to Webhooks

Return an HTTP `200` status code as quickly as possible — ideally within **5 seconds**. Swytcho considers any non-`2xx` response (or a timeout) a failed delivery and schedules a retry. If you need to do heavy processing, acknowledge the webhook immediately and handle the work asynchronously in a background job or queue.

## Retry Behavior

If your endpoint fails to return a `2xx` response, Swytcho automatically retries the delivery up to **5 times** using exponential backoff:

| Attempt | Delay after previous failure |
| ------- | ---------------------------- |
| 1       | 30 seconds                   |
| 2       | 2 minutes                    |
| 3       | 10 minutes                   |
| 4       | 30 minutes                   |
| 5       | 2 hours                      |

After 5 failed attempts, the delivery is marked as permanently failed and no further retries are made. You can view failed deliveries and manually retry them from **Settings → Webhooks** in the dashboard.

<Tip>
  Use a tool like [webhook.site](https://webhook.site) or [Svix Play](https://play.svix.com) to inspect and debug incoming webhook payloads during local development — no tunneling setup required.
</Tip>
