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

# Feedback Webhook

> Thrad POSTs user feedback signals (thumbs up / thumbs down) to your DSP in real time whenever a user rates an ad you served.

Thrad always records user feedback internally to power ad relevancy and guarantee a consistent user experience — regardless of whether you opt into forwarding. If you provide a `feedback_url` in your creative, Thrad additionally forwards the signal to your endpoint so you can close the attribution loop in your own optimisation stack.

<Note>
  This is a **webhook Thrad sends to you**, not an endpoint you call. You implement the receiver; Thrad fires the POST.
</Note>

## How it works

```
User rates ad (👍 or 👎)
        ↓
Publisher SDK → Thrad SSP
        ↓
Thrad records feedback internally (ad relevancy + UX guarantee)
        ↓  ← only if feedback_url was in your creative
Thrad → POST {your feedback_url}  (fire-and-forget, async)
```

## Opting in

Include `feedback_url` inside your creative — either in `ad_data` (pre-rendered bid) or in the render response. The URL is per-bid, so embed whatever you need to route and attribute the signal on your side.

**Pre-rendered bid:**

```json theme={null}
{
  "data": {
    "bid": 7.50,
    "bidId": "bid_abc123",
    "ad_data": {
      "placement": "image",
      "headline": "...",
      "url": "...",
      "image_url": "...",
      "feedback_url": "https://your-dsp.com/feedback?bid=bid_abc123"
    }
  }
}
```

**Render response (two-step flow):**

```json theme={null}
{
  "data": {
    "placement": "image",
    "headline": "...",
    "url": "...",
    "image_url": "...",
    "feedback_url": "https://your-dsp.com/feedback?bid=bid_abc123"
  }
}
```

## Request

Thrad fires a `POST` to your `feedback_url`:

### Headers

| Header            | Value                               |
| ----------------- | ----------------------------------- |
| `Content-Type`    | `application/json`                  |
| `X-Forwarded-For` | End-user IP from the publisher side |
| `X-User-Agent`    | End-user UA from the publisher side |

### Body

```json theme={null}
{
  "bid_id": "bid_abc123",
  "feedback_type": "positive",
  "feedback_text": "",
  "feedback_timestamp": "2026-05-08T15:30:00+00:00"
}
```

| Field                | Type                         | Description                                              |
| -------------------- | ---------------------------- | -------------------------------------------------------- |
| `bid_id`             | string                       | The `bidId` from your bid response                       |
| `feedback_type`      | `"positive"` \| `"negative"` | Thumbs up or thumbs down                                 |
| `feedback_text`      | string                       | Optional free-text comment from the user (usually empty) |
| `feedback_timestamp` | ISO 8601                     | When the feedback was submitted                          |

## Response

Return any `2xx` within **5 seconds**. Acknowledge immediately and process async:

```python theme={null}
@app.post("/feedback")
async def receive_feedback(request: Request):
    event = await request.json()
    await queue.enqueue(event)
    return {"ok": True}
```

## Timeouts and retries

* Thrad times out after **5 seconds**.
* **No retries.** If your endpoint is down the forwarded event is dropped. Thrad's internal record is always written regardless.
