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

# Webhooks

> Receive real-time notifications when generations complete instead of polling.

Webhooks deliver generation results to your server in real-time. Instead of polling `GET /api/generations/:id` repeatedly, you receive a single `POST` request when the generation is done.

## Polling vs. webhooks

|                | Polling                      | Webhooks                         |
| -------------- | ---------------------------- | -------------------------------- |
| **Setup**      | No setup needed              | Requires a public HTTPS endpoint |
| **Latency**    | Depends on poll interval     | Near-instant notification        |
| **Efficiency** | Many requests per generation | One request per generation       |
| **Best for**   | Quick scripts, testing       | Production applications          |

## How to use webhooks

Add a `webhook` URL to your `/api/run` request:

```bash theme={null}
curl -X POST https://api.artificialstudio.ai/api/run \
  -H "Content-Type: application/json" \
  -H "Authorization: YOUR_API_KEY" \
  -d '{
    "tool": "create-image",
    "input": {
      "prompt": "A mountain landscape at dawn"
    },
    "webhook": "https://your-server.com/webhook"
  }'
```

<Warning>Webhook URLs must use HTTPS. Private or internal URLs (localhost, private IPs) are rejected for security.</Warning>

## Webhook payload

When a generation completes successfully, we send a `POST` request to your URL with these headers and body:

### Headers

| Header             | Value               |
| ------------------ | ------------------- |
| `Content-Type`     | `application/json`  |
| `X-Webhook-Source` | `artificial-studio` |
| `X-Media-Id`       | The generation ID   |

### Body

```json theme={null}
{
  "id": "507f1f77bcf86cd799439011",
  "status": "success",
  "tool": "create-image",
  "type": "image",
  "output": "https://files.artificialstudio.ai/generations/abc123.png",
  "thumbnail": "https://files.artificialstudio.ai/thumbnails/abc123.jpg",
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```

| Field       | Type           | Description                                          |
| ----------- | -------------- | ---------------------------------------------------- |
| `id`        | string         | Generation identifier                                |
| `status`    | string         | Always `success` (webhooks are only sent on success) |
| `tool`      | string         | Tool used                                            |
| `type`      | string         | `image`, `video`, `audio`, `text`, or `object`       |
| `output`    | string         | URL to the generated media                           |
| `thumbnail` | string \| null | Thumbnail URL (null for audio)                       |
| `createdAt` | string         | ISO 8601 timestamp                                   |

## Example handlers

<CodeGroup>
  ```javascript Express theme={null}
  app.post('/webhook', express.json(), (req, res) => {
    const { id, output, tool, type } = req.body;

    console.log(`Generation ${id} completed: ${output}`);
    // Save to database, notify user, etc.

    res.status(200).send('OK');
  });
  ```

  ```python Flask theme={null}
  from flask import Flask, request

  app = Flask(__name__)

  @app.route('/webhook', methods=['POST'])
  def webhook():
      data = request.json

      print(f"Generation {data['id']} completed: {data['output']}")
      # Save to database, notify user, etc.

      return 'OK', 200
  ```

  ```javascript Next.js theme={null}
  export async function POST(request) {
    const data = await request.json();

    console.log(`Generation ${data.id} completed: ${data.output}`);
    // Save to database, notify user, etc.

    return new Response('OK', { status: 200 });
  }
  ```
</CodeGroup>

## Retries

If your endpoint does not return a `200` status, we retry with exponential backoff:

| Retry | Delay      |
| ----- | ---------- |
| 1     | 5 seconds  |
| 2     | 15 seconds |
| 3     | 1 minute   |
| 4     | 5 minutes  |
| 5     | 15 minutes |
| 6     | 1 hour     |
| 7     | 6 hours    |
| 8     | 12 hours   |
| 9     | 24 hours   |

After 9 failed retries, the webhook is marked as failed.

## Best practices

<Note>
  **Return a 200 quickly.** Your endpoint should respond within 5 seconds. Process the webhook data asynchronously if needed.
</Note>

* **Handle duplicates** — Retries may deliver the same webhook twice. Use the `id` field to deduplicate.
* **Validate the payload** — Check that the data matches expected formats before processing.
* **Log everything** — Keep logs of received webhooks for debugging.

## Requirements

Your webhook endpoint must:

* Be **publicly accessible** (no localhost or private IPs)
* Use **HTTPS**
* Accept **POST** requests with a JSON body
* Return **200** to acknowledge receipt
