# Webhooks

Receive real-time notifications about events during client interactions with Amigo.

{% hint style="info" %}
**Classic API** - These webhook endpoints are organization-scoped under the Classic API at `api.amigo.ai`.
{% endhint %}

{% hint style="info" %}
**Real-time Events** Webhooks enable your application to react immediately to conversation events, post-processing completions, and other system activities.
{% endhint %}

## Webhook Destinations

{% tabs %}
{% tab title="Configuration" %}

| Feature                  | Details                                        |
| ------------------------ | ---------------------------------------------- |
| **Maximum Destinations** | 10 per organization                            |
| **Event Filtering**      | Specify which event types to receive           |
| **Retry Configuration**  | Customize retry attempts for failed deliveries |
| **Secret Management**    | Unique secret per destination for security     |
| {% endtab %}             |                                                |

{% tab title="Capabilities" %}

* Real-time event notifications
* Selective event subscription
* Automatic retry with exponential backoff
* Signature verification for security
* Zero-downtime secret rotation
  {% endtab %}
  {% endtabs %}

#### Managing Webhook Destinations

Use these endpoints to manage your webhook destinations

{% openapi src="<https://api.amigo.ai/v1/openapi.json>" path="/v1/{organization}/webhook\_destination/" method="post" %}
<https://api.amigo.ai/v1/openapi.json>
{% endopenapi %}

{% openapi src="<https://api.amigo.ai/v1/openapi.json>" path="/v1/{organization}/webhook\_destination/{webhook\_destination\_id}" method="delete" %}
<https://api.amigo.ai/v1/openapi.json>
{% endopenapi %}

{% openapi src="<https://api.amigo.ai/v1/openapi.json>" path="/v1/{organization}/webhook\_destination/" method="get" %}
<https://api.amigo.ai/v1/openapi.json>
{% endopenapi %}

{% openapi src="<https://api.amigo.ai/v1/openapi.json>" path="/v1/{organization}/webhook\_destination/{webhook\_destination\_id}" method="post" %}
<https://api.amigo.ai/v1/openapi.json>
{% endopenapi %}

{% hint style="info" %}
**URL Cannot Be Changed** The URL of a webhook destination cannot be updated. To change the URL, create a new webhook destination and delete the old one.
{% endhint %}

{% openapi src="<https://api.amigo.ai/v1/openapi.json>" path="/v1/{organization}/webhook\_destination/{webhook\_destination\_id}/rotate-secret" method="post" %}
<https://api.amigo.ai/v1/openapi.json>
{% endopenapi %}

{% hint style="info" %}
**Rate Limit** Secret rotation can occur at most once per hour for each webhook destination.
{% endhint %}

{% openapi src="<https://api.amigo.ai/v1/openapi.json>" path="/v1/{organization}/webhook\_destination/{webhook\_destination\_id}/delivery" method="get" %}
<https://api.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Delivery & Retries

{% hint style="warning" %}
**Automatic Retry Logic**\
Failed webhook deliveries are automatically retried with exponential backoff to ensure reliable event delivery.
{% endhint %}

| Retry Attempt | Delay | Total Time |
| ------------- | ----- | ---------- |
| Initial       | 0s    | Immediate  |
| Retry 1       | 5s    | 5s         |
| Retry 2       | 10s   | 15s        |
| Retry 3       | 20s   | 35s        |
| Retry 4       | 20s   | 55s        |

* Configure retry attempts via `retry_attempts` parameter
* View delivery history for past 30 days via API

#### Delivery Sequence

{% @mermaid/diagram content="%%{init: {"theme": "base", "themeVariables": {"actorBkg": "#083241", "actorTextColor": "#FFFFFF", "actorBorder": "#083241", "signalColor": "#575452", "signalTextColor": "#100F0F", "labelBoxBkgColor": "#F1EAE7", "labelBoxBorderColor": "#D7D2D0", "labelTextColor": "#100F0F", "loopTextColor": "#100F0F", "noteBkgColor": "#F1EAE7", "noteBorderColor": "#D7D2D0", "noteTextColor": "#100F0F", "activationBkgColor": "#E8E2EB", "activationBorderColor": "#083241", "altSectionBkgColor": "#F1EAE7", "altSectionColor": "#100F0F"}}}%%
sequenceDiagram
autonumber
participant A as Amigo Events
participant W as Webhook Destination (Your Endpoint)

A->>W: POST /webhook<br/>{ event, headers, signature }
alt 2xx ack
W-->>A: 2xx Accepted/OK
else Non-2xx / timeout
W-->>A: 5xx/timeout
A->>W: Retry 1 (5s backoff)
W-->>A: 5xx/timeout
A->>W: Retry 2 (10s backoff)
A->>W: Retry 3 (20s backoff)
A->>W: Retry 4 (20s backoff)
end

Note over A,W: `x-amigo-idempotent-key` stays constant<br/>across retries for deduplication" %}

## Security

### Webhook Signatures

Every webhook request is cryptographically signed to ensure authenticity.

{% hint style="danger" %}
**Secret Storage**\
Store webhook secrets securely. Never commit them to version control or expose them in client-side code.
{% endhint %}

**Signature Process:**

1. Receive secret when creating webhook destination
2. Amigo signs all requests with HMAC-SHA256
3. Validate signature on receipt to verify authenticity

### Request Headers

| Header                      | Description                                                    |
| --------------------------- | -------------------------------------------------------------- |
| `x-amigo-idempotent-key`    | Unique identifier persisting through retries for deduplication |
| `x-amigo-request-timestamp` | UNIX timestamp (milliseconds) of the delivery attempt          |
| `x-amigo-request-signature` | HMAC-SHA256 signature of `v1:{timestamp}:{body}`               |

### Signature Verification

{% tabs %}
{% tab title="Node.js Example" %}

```javascript
const crypto = require('crypto');

function verifyWebhookSignature(body, timestamp, signature, secret) {
  // 1. Concatenate version, timestamp, and body
  const payload = `v1:${timestamp}:${body}`;
  
  // 2. Compute HMAC-SHA256
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  // 3. Compare signatures
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}
```

{% endtab %}

{% tab title="Python Example" %}

```python
import hmac
import hashlib

def verify_webhook_signature(body, timestamp, signature, secret):
    # 1. Concatenate version, timestamp, and body
    payload = f"v1:{timestamp}:{body}"
    
    # 2. Compute HMAC-SHA256
    expected_signature = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    
    # 3. Compare signatures
    return hmac.compare_digest(signature, expected_signature)
```

{% endtab %}
{% endtabs %}

### Secret Rotation

{% hint style="success" %}
**Zero-Downtime Rotation**\
Dual-signing period allows smooth secret rotation without missing events.
{% endhint %}

**Rotation Process:**

| Step | Action                    | Duration                      |
| ---- | ------------------------- | ----------------------------- |
| 1    | Call rotation endpoint    | Immediate                     |
| 2    | Receive new secret        | Immediate                     |
| 3    | Dual-signing begins       | 30 minutes                    |
| 4    | Update verification logic | During dual-signing           |
| 5    | Old secret expires        | After `dual_signing_stops_at` |

**During dual-signing period:**

* Amigo sends two signatures per request
* Accept either old or new signature
* No webhook deliveries are lost

#### Rotation States

{% @mermaid/diagram content="%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#D4E2E7", "primaryTextColor": "#100F0F", "primaryBorderColor": "#083241", "lineColor": "#575452", "textColor": "#100F0F"}}}%%
stateDiagram-v2
direction LR
OldActive: Old secret active
DualSigning: Dual-signing window
NewActive: New secret active

\[*] --> OldActive
OldActive --> DualSigning: rotate secret
DualSigning --> NewActive: dual\_signing\_stops\_at
NewActive --> \[*]

note right of DualSigning
Both old and new signatures<br/>are accepted during this window
end note" %}

## Webhook Event Types

### API Key Events

#### `api-key-expiration-soon`

Triggered when an API key is approaching its expiration date. Notifications fire at 14, 7, and 1 day before expiration, enabling proactive key rotation and preventing service disruptions.

**Payload structure:**

```javascript
{
    // The webhook event type
    type: "api-key-expiration-soon",

    // Your organization's unique identifier
    org_id: string,

    // The API key that is expiring
    api_key_id: string,

    // When the API key will expire (UTC datetime)
    expiration_time: string,

    // Unique key for deduplication across retries
    idempotent_key: string
}
```

{% hint style="success" %}
**Proactive Key Rotation** Subscribe to this event to automate key rotation workflows. Create a new API key, transition your applications, and revoke the expiring key before it becomes invalid.
{% endhint %}

### Post-Processing Events

#### `conversation-post-processing-complete`

Triggered when asynchronous post-processing analysis completes for a conversation.

{% hint style="info" %}
**Asynchronous Processing**\
Post-processing runs after conversations end, performing deep analysis without blocking the user experience.
{% endhint %}

**Payload structure:**

```javascript
{
    // The webhook event type
    type: "conversation-post-processing-complete",

    // The specific post-processing operation that completed
    post_processing_type: "generate-tasks" | "generate-user-models" | "extract-memories" | "compute-metrics",

    // Unique identifier for the conversation that was analyzed
    conversation_id: string,

    // Your organization's unique identifier
    org_id: string
}
```

**Post-Processing Types:**

| Type                   | Description                                      | Purpose                               |
| ---------------------- | ------------------------------------------------ | ------------------------------------- |
| `generate-user-models` | Updates L2 user model with conversation insights | Enhanced personalization              |
| `extract-memories`     | Stores key information as L1 observations        | Future context recall                 |
| `compute-metrics`      | Calculates performance metrics                   | Quality and effectiveness measurement |

<details>

<summary>Processing Architecture</summary>

* **Live Sessions**: Handle real-time interaction
* **Post-Processing**: Performs deep analysis after conversation
* **No Blocking**: User experience remains responsive
* **Async Updates**: Memories, models, and metrics updated in background

</details>
