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

# Webhooks

> Ontvang real-time meldingen wanneer asynchrone operaties voltooid zijn

<Check>
  **We breiden de ondersteuning voor webhooks actief uit.**

  Net gelanceerd: automatische herhalingen met exponentiële backoff — mislukte leveringen worden nu tot 5 keer opnieuw geprobeerd binnen 30 minuten.

  **Binnenkort beschikbaar:**

  * Team-brede standaard webhook-URL's
  * Cryptografische handtekeningen voor payloadverificatie

  Wil je vroegtijdige toegang? Neem contact op via [info@olostep.com](mailto:info@olostep.com) of sluit je aan bij onze [Slack-community](https://olostep-users.slack.com/join/shared_invite/zt-2bfddyi8h-JzfjOgavg~98DJ1om1B5Lg).
</Check>

## Overzicht

Webhooks leveren real-time HTTP POST-meldingen aan je server wanneer langlopende operaties voltooid zijn. In plaats van de status te polleren, ontvangt je applicatie directe updates.

### Gebruiksscenario's

<CardGroup cols={2}>
  <Card title="Asynchrone Verwerking" icon="clock">
    Ontvang een melding wanneer batches of crawls voltooid zijn in plaats van te polleren
  </Card>

  <Card title="Pipeline Triggers" icon="diagram-project">
    Start automatisch downstream verwerking wanneer data gereed is
  </Card>

  <Card title="Waarschuwingen" icon="bell">
    Stuur waarschuwingen naar Slack, e-mail of andere systemen bij voltooiing
  </Card>

  <Card title="Data Synchronisatie" icon="arrows-rotate">
    Houd je database gesynchroniseerd met Olostep-resultaten
  </Card>
</CardGroup>

## Ondersteunde Evenementen

<AccordionGroup>
  <Accordion title="batch.completed" icon="layer-group">
    Wordt geactiveerd wanneer een batch klaar is met verwerken (alle items voltooid of mislukt).

    ```json theme={null}
    {
      "id": "event_a1b2c3d4e5f6g7h8",
      "object": "event.batch.completed",
      "timestamp": 1737570000000,
      "delivery_attempt": "1/5",
      "data": {
        "id": "batch_xyz123",
        "object": "batch",
        "status": "completed",
        "items_total": 100,
        "items_completed": 98,
        "items_failed": 2,
        "created_at": "2024-01-15T10:00:00Z",
        "completed_at": "2024-01-15T10:05:32Z"
      }
    }
    ```
  </Accordion>

  <Accordion title="crawl.completed" icon="spider-web">
    Wordt geactiveerd wanneer een crawl klaar is en alle ontdekte pagina's zijn verwerkt.

    ```json theme={null}
    {
      "id": "event_x9y8z7w6v5u4t3s2",
      "object": "event.crawl.completed",
      "timestamp": 1737570000000,
      "delivery_attempt": "1/5",
      "data": {
        "id": "crawl_abc789",
        "object": "crawl",
        "status": "completed",
        "start_url": "https://example.com",
        "urls_count": 87,
        "max_pages": 100,
        "max_depth": 3,
        "actual_max_depth": 3,
        "start_epoch": 1737569500000,
        "start_date": "2024-01-15"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Webhooks Instellen

Geef `webhook` door bij het aanmaken van een resource. Deze URL ontvangt de voltooiingsmelding.

<Note>
  **Parameternaam:** De canonieke parameter is `webhook`. Voor achterwaartse compatibiliteit wordt `webhook_url` ook geaccepteerd als alias.
</Note>

<CodeGroup>
  ```python Python theme={null}
  import requests

  # Batch voorbeeld
  response = requests.post(
      "https://api.olostep.com/v1/batches",
      headers={"Authorization": "Bearer <YOUR_API_KEY>"},
      json={
          "items": [
              {"url": "https://example.com/page1", "custom_id": "1"},
              {"url": "https://example.com/page2", "custom_id": "2"}
          ],
          "webhook": "https://your-server.com/webhooks/olostep"
      }
  )

  # Crawl voorbeeld
  response = requests.post(
      "https://api.olostep.com/v1/crawls",
      headers={"Authorization": "Bearer <YOUR_API_KEY>"},
      json={
          "start_url": "https://example.com",
          "max_pages": 50,
          "webhook": "https://your-server.com/webhooks/olostep"
      }
  )
  ```

  ```js Node theme={null}
  // Batch voorbeeld
  const batchResponse = await fetch('https://api.olostep.com/v1/batches', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer <YOUR_API_KEY>',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      items: [
        { url: 'https://example.com/page1', custom_id: "1" },
        { url: 'https://example.com/page2', custom_id: "2" }
      ],
      webhook: 'https://your-server.com/webhooks/olostep'
    })
  });

  // Crawl voorbeeld
  const crawlResponse = await fetch('https://api.olostep.com/v1/crawls', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer <YOUR_API_KEY>',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      start_url: 'https://example.com',
      max_pages: 50,
      webhook: 'https://your-server.com/webhooks/olostep'
    })
  });
  ```

  ```bash cURL theme={null}
  # Batch voorbeeld
  curl -X POST "https://api.olostep.com/v1/batches" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "items": [
        {"url": "https://example.com/page1", "custom_id": "1"},
        {"url": "https://example.com/page2", "custom_id": "2"}
      ],
      "webhook": "https://your-server.com/webhooks/olostep"
    }'

  # Crawl voorbeeld
  curl -X POST "https://api.olostep.com/v1/crawls" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "start_url": "https://example.com",
      "max_pages": 50,
      "webhook": "https://your-server.com/webhooks/olostep"
    }'
  ```
</CodeGroup>

***

## Webhook Payload

Alle webhook payloads volgen een uniforme envelopstructuur:

```json theme={null}
{
  "id": "event_a1b2c3d4e5f6g7h8",
  "object": "event.batch.completed",
  "timestamp": 1737570000000,
  "delivery_attempt": "1/5",
  "data": {
    "id": "batch_xyz123",
    "object": "batch",
    "status": "completed",
    "items_total": 100,
    "items_completed": 98,
    "items_failed": 2
  }
}
```

### Envelopvelden

| Veld               | Beschrijving                                                        |
| ------------------ | ------------------------------------------------------------------- |
| `id`               | Evenement-ID — **hetzelfde bij alle herhalingspogingen**            |
| `object`           | Type evenement (bijv. `event.batch.completed`)                      |
| `timestamp`        | Wanneer deze leveringspoging is verzonden (epoch ms)                |
| `delivery_attempt` | Huidige poging / max pogingen (bijv. `1/5`, `3/5`)                  |
| `data`             | De daadwerkelijke resourcegegevens (zelfde formaat als API-respons) |

<Tip>
  Gebruik het `id` veld om webhook leveringen in je ontvanger te dedupliceren. Dezelfde evenement-ID verschijnt in alle herhalingspogingen.
</Tip>

***

## Herhalingsgedrag

Mislukte webhook leveringen worden automatisch opnieuw geprobeerd met exponentiële backoff over een venster van 30 minuten:

| Poging | Vertraging voor poging | Cumulatieve tijd |
| ------ | ---------------------- | ---------------- |
| 1      | Onmiddellijk           | 0 min            |
| 2      | \~2 min                | \~2 min          |
| 3      | \~4 min                | \~6 min          |
| 4      | \~7 min                | \~13 min         |
| 5      | \~15 min               | \~28 min         |

**Totale herhalingsvenster:** 30 minuten\
**Timeout per verzoek:** 30 seconden

### Wat telt als succes

Je eindpunt moet binnen 30 seconden een `2xx` statuscode retourneren. Elke andere respons triggert een herhaling.

| Respons              | Resultaat                             |
| -------------------- | ------------------------------------- |
| `200 OK`             | ✅ Geleverd                            |
| `201 Created`        | ✅ Geleverd                            |
| `301 Redirect`       | ❌ Herhalen (we volgen geen redirects) |
| `400 Bad Request`    | ❌ Herhalen                            |
| `500 Server Error`   | ❌ Herhalen                            |
| Timeout (>30s)       | ❌ Herhalen                            |
| Verbinding geweigerd | ❌ Herhalen                            |

***

## Best Practices

<AccordionGroup>
  <Accordion title="Reageer snel, verwerk asynchroon">
    Retourneer `200 OK` onmiddellijk en verwerk de webhook asynchroon. Als je verwerking langer dan 30 seconden duurt, proberen we opnieuw — wat dubbele leveringen veroorzaakt.

    ```python theme={null}
    from queue import Queue
    import threading

    webhook_queue = Queue()

    @app.route('/webhooks/olostep', methods=['POST'])
    def handle_webhook():
        # In de wachtrij voor asynchrone verwerking
        webhook_queue.put(request.json)
        
        # Onmiddellijk retourneren
        return 'OK', 200

    def process_webhooks():
        while True:
            event = webhook_queue.get()
            # Langzame verwerking gebeurt hier
            process_event(event)

    threading.Thread(target=process_webhooks, daemon=True).start()
    ```
  </Accordion>

  <Accordion title="Implementeer idempotente handlers">
    Gebruik het `id` veld om te dedupliceren. Sla verwerkte evenement-ID's op en sla duplicaten over.

    ```python theme={null}
    processed_events = set()  # Gebruik Redis/DB in productie

    def handle_event(event):
        if event['id'] in processed_events:
            return  # Al verwerkt
        
        # Verwerk het evenement
        process_batch_completed(event['data'])
        
        # Markeer als verwerkt
        processed_events.add(event['id'])
    ```
  </Accordion>

  <Accordion title="Log webhook ontvangsten">
    Log alle webhook ontvangsten voor debugging. Inclusief de evenement-ID, timestamp en verwerkingsresultaat.

    ```python theme={null}
    import logging

    @app.route('/webhooks/olostep', methods=['POST'])
    def handle_webhook():
        event = request.json
        logging.info(f"Webhook ontvangen: id={event['id']} type={event['object']} poging={event['delivery_attempt']}")
        
        try:
            process_event(event)
            logging.info(f"Webhook verwerkt: id={event['id']}")
        except Exception as e:
            logging.error(f"Webhook mislukt: id={event['id']} fout={e}")
            raise
        
        return 'OK', 200
    ```
  </Accordion>

  <Accordion title="Gebruik HTTPS eindpunten">
    Gebruik altijd HTTPS voor webhook eindpunten. HTTP eindpunten zijn kwetsbaar voor afluisteren en man-in-the-middle aanvallen.
  </Accordion>
</AccordionGroup>

***

## Problemen Oplossen

<AccordionGroup>
  <Accordion title="Geen webhooks ontvangen">
    1. Controleer of de `webhook` parameter in je verzoek is opgenomen
    2. Controleer of je eindpunt openbaar toegankelijk is (niet localhost)
    3. Controleer je serverlogs op binnenkomende verzoeken
    4. Zorg ervoor dat je een `2xx` statuscode retourneert
  </Accordion>

  <Accordion title="Dubbele webhooks ontvangen">
    Dit is te verwachten tijdens herhalingen. Implementeer idempotente verwerking met behulp van het `id` veld:

    ```python theme={null}
    def handle_event(event):
        if already_processed(event['id']):
            return  # Sla duplicaat over
        
        process_event(event)
        mark_processed(event['id'])
    ```
  </Accordion>

  <Accordion title="Webhooks verlopen">
    Je eindpunt moet binnen 30 seconden reageren. Verwerk webhooks asynchroon:

    ```python theme={null}
    @app.route('/webhooks', methods=['POST'])
    def webhook():
        queue.enqueue(process_webhook, request.json)
        return 'OK', 200  # Reageer onmiddellijk
    ```
  </Accordion>
</AccordionGroup>

***

## Binnenkort Beschikbaar

<CardGroup cols={2}>
  <Card title="Team Standaard URL" icon="gear">
    Stel een standaard webhook-URL in in je accountinstellingen. Alle verzoeken zullen deze URL gebruiken, tenzij overschreven.
  </Card>

  <Card title="Handtekeningverificatie" icon="shield-check">
    Cryptografische handtekeningen (HMAC-SHA256) om te verifiëren dat webhook payloads van Olostep komen.
  </Card>
</CardGroup>

<Note>
  Wil je vroegtijdige toegang tot deze functies? Neem contact op via [info@olostep.com](mailto:info@olostep.com) of sluit je aan bij onze [Slack-community](https://olostep-users.slack.com/join/shared_invite/zt-2bfddyi8h-JzfjOgavg~98DJ1om1B5Lg).
</Note>
