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

# Get started

> Set up outbound webhooks.

You can provide a webhook URL when using any of the create endpoints: [Create Scout](./scouts-create), [Create Research](./research-create), or [Create Browsing](./browsing-create).

The scout\_webhook format is a structured JSON format that provides nested data.

If you want Slack-compatible payloads, provide a Slack incoming webhook URL and set `webhook_format` to
`slack` when creating or updating the scout. Slack payloads use Block Kit formatting.

### Example Request

Below is an example of the HTTP request that a Scout sends to your webhook endpoint.

```http theme={null}
POST https://your-webhook-endpoint.com/webhook
Content-Type: application/json
User-Agent: Scout-Webhook/1.0
X-Scout-Event: scout.update
```

```json theme={null}
{
  "event_type": "scout_update",
  "scout": {
    "id": "scout-123",
    "display_name": "Reddit Posts Monitor",
    "query": "Posts on r/sanfrancisco about new offices opening in downtown sf"
  },
  "update": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2024-08-05T15:30:45.123456Z",
    "status": "completed",
    "has_changes": true,
    "summary": "Found 3 new posts about office openings in downtown SF",
    "details_url": "https://platform.yutori.com/scouting/tasks/scout-123",
    "report_content": "..."
  },
  "delivery": {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "attempt": 1,
    "timestamp": "2024-08-05T15:30:45.123456Z"
  }
}
```

### Slack Incoming Webhook Payload (Example)

```json theme={null}
{
  "text": "Scout 'Reddit Posts Monitor' has new updates",
  "blocks": [
    {
      "type": "header",
      "text": {
        "type": "plain_text",
        "text": "Scout Update: Reddit Posts Monitor",
        "emoji": true
      }
    },
    {
      "type": "section",
      "fields": [
        { "type": "mrkdwn", "text": "*Query:*\nPosts on r/sanfrancisco about new offices opening in downtown sf" },
        { "type": "mrkdwn", "text": "*Status:*\nHas New Updates" }
      ]
    }
  ]
}
```

## Field Descriptions

| Field                   | Description                                                             |
| ----------------------- | ----------------------------------------------------------------------- |
| `event_type`            | Always `"scout_update"` for all APIs (Scouting, Research, and Browsing) |
| `scout.id`              | Unique identifier for the Scout                                         |
| `scout.display_name`    | Human-readable name                                                     |
| `scout.query`           | Original query/task description                                         |
| `update.id`             | Unique identifier for this update                                       |
| `update.timestamp`      | ISO 8601 timestamp when update was generated                            |
| `update.status`         | Typically `"completed"`                                                 |
| `update.has_changes`    | Boolean indicating if new content was found                             |
| `update.summary`        | Brief description of what was found                                     |
| `update.details_url`    | URL to view full results on `platform.yutori.com`                       |
| `update.report_content` | Raw content of the update                                               |
| `delivery.id`           | Unique identifier for this delivery attempt                             |
| `delivery.attempt`      | Delivery attempt number (starts at 1)                                   |
| `delivery.timestamp`    | Timestamp when webhook was sent                                         |

## Webhook Delivery Details

### Headers

All webhook requests include the following headers:

* `Content-Type: application/json`
* `User-Agent: Scout-Webhook/1.0`
* `X-Scout-Event: scout.update`

#### Response Expectations

Your webhook endpoint should:

* Respond with HTTP status **200-299** for successful receipt
* Process the webhook asynchronously if needed
* Respond within **10 seconds** per delivery attempt

#### Error Handling

Scout will consider delivery failed if:

* HTTP response status is not 2xx
* Request times out (default 10 seconds)
* Network error occurs

Retryable failures (timeouts, network errors, and HTTP `408`, `409`, `425`, `429`, `500`, `502`, `503`,
and `504`) are retried up to 3 total attempts with short exponential backoff. With the default 10-second
timeout, timeout-based failures complete in roughly 30-35 seconds.

If all attempts fail, the Scout finding remains available in Yutori, but webhook delivery is not queued for
later redelivery.

#### Security Considerations

* Use **HTTPS** URLs for production webhooks
* **HTTP** URLs are only allowed for `localhost`/`127.0.0.1` (testing)
* Validate webhook payload structure in your endpoint
* Consider implementing webhook signature verification for additional security

## Smoke Test

You can send a test webhook from the API using your API key:

```bash theme={null}
curl --request POST \
  --url https://api.yutori.com/v1/scouting/webhooks/test \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "webhook_url": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
    "webhook_format": "slack"
  }'
```

## Example Integration Code

### Python (Flask)

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

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def handle_scout_webhook():
    payload = request.get_json()

    # Handle scout_webhook format
    if payload.get('event_type') == 'scout_update':
        scout_name = payload['scout']['display_name']
        has_changes = payload['update']['has_changes']
        summary = payload['update']['summary']

        print(f"Scout '{scout_name}' update: {summary}")

    return jsonify({'status': 'received'}), 200

```

### Node.js (Express)

```javascript theme={null}
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhook', (req, res) => {
    const payload = req.body;

    // Handle scout_webhook format
    if (payload.event_type === 'scout_update') {
        const scoutName = payload.scout.display_name;
        const hasChanges = payload.update.has_changes;
        const summary = payload.update.summary;

        console.log(`Scout '${scoutName}' update: ${summary}`);
    }
    res.status(200).json({ status: 'received' });
});

app.listen(3000, () => {
    console.log('Webhook server listening on port 3000');
});

```
