> ## 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.

# Render Ad

> Endpoint that DSPs must implement to receive render requests from Thrad SSP (ssp.thrads.ai). Called by the SSP only if this DSP won the auction.

<Note>
  **Implementation Required**: Your DSP must implement this endpoint on your own server. The SSP at `ssp.thrads.ai` will send POST requests to your configured DSP endpoint URL.
</Note>

## Authorizations

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`
</ParamField>

<Note>
  This endpoint does not require API key authentication when called by SSP. Authentication is handled at the SSP level.
</Note>

## Body

<ParamField body="bidId" type="string" required>
  The bid identifier from the winning bid response. Must match a valid bid in cache (TTL: \~60 seconds).
</ParamField>

<ParamField body="realizedPrice" type="float" required>
  The actual clearing price from the auction (what the winner pays). In first-price auctions, this matches the winning bid amount.
</ParamField>

<ParamField body="production" type="boolean" default="true">
  Whether the request is for production. Defaults to `true`.
</ParamField>

<RequestExample>
  ```json Example Request theme={null}
  {
    "bidId": "bid_abc123",
    "realizedPrice": 7.50,
    "production": true
  }
  ```
</RequestExample>

<ResponseExample>
  ```json 200 - Sponsored Message (placement=image) theme={null}
  {
    "data": {
      "ad_format": "sponsored_message",
      "placement": "image",
      "advertiser": "Nike",
      "domain": "nike.com",
      "headline": "Check out the Nike Air Zoom Alphafly",
      "description": "The marathon record-holder's shoe.",
      "cta_text": "Shop Now",
      "url": "https://your-dsp.com/click/token_abc123",
      "image_url": "https://cdn.nike.com/alphafly.png",
      "logo_url": "https://cdn.nike.com/logo.png",
      "view_url": "https://your-dsp.com/view/token_abc123",
      "feedback_url": "https://your-dsp.com/feedback?bid=bid_abc123"
    }
  }
  ```

  ```json 200 - Sponsored Message (placement=text) theme={null}
  {
    "data": {
      "ad_format": "sponsored_message",
      "placement": "text",
      "advertiser": "Acme Sports",
      "domain": "acmesports.com",
      "headline": "Best Running Shoes 2026",
      "description": "Get 20% off premium running shoes today.",
      "cta_text": "Shop Now",
      "url": "https://your-dsp.com/click/token_abc123",
      "logo_url": "https://cdn.acmesports.com/logo.png",
      "view_url": "https://your-dsp.com/view/token_abc123",
      "feedback_url": "https://your-dsp.com/feedback?bid=bid_abc123"
    }
  }
  ```

  ```json 200 - Carousel theme={null}
  {
    "data": {
      "ad_format": "sponsored_carousel",
      "advertiser": "Nike",
      "domain": "nike.com",
      "items": [
        {
          "headline": "Pegasus Trail 5",
          "description": "Cushioned trail runner.",
          "image_url": "https://cdn.nike.com/p1.png",
          "url": "https://your-dsp.com/click/token_abc123?tile=0"
        },
        {
          "headline": "Vomero 17",
          "image_url": "https://cdn.nike.com/p2.png",
          "url": "https://your-dsp.com/click/token_abc123?tile=1"
        },
        {
          "headline": "Invincible 3",
          "image_url": "https://cdn.nike.com/p3.png",
          "url": "https://your-dsp.com/click/token_abc123?tile=2"
        }
      ],
      "view_url": "https://your-dsp.com/view/token_abc123",
      "feedback_url": "https://your-dsp.com/feedback?bid=bid_abc123"
    }
  }
  ```

  ```json 404 - Bid Not Found theme={null}
  {
    "error": "Bid not found"
  }
  ```

  ```json 400 - Validation Error theme={null}
  {
    "error": "Validation error: description is required"
  }
  ```

  ```json 500 - Error theme={null}
  {
    "error": "Internal Server Error"
  }
  ```
</ResponseExample>

## Response

<ResponseField name="data" type="object" required>
  Ad creative response payload. Shape is discriminated by `ad_format`. **`ad_format` defaults to `"sponsored_message"` if omitted** — only required when returning a non-default format like `"sponsored_carousel"`.

  <Expandable title="data properties (sponsored_message, placement=image)">
    <Note>
      Full card with a hero product image. Use when you have a real product photo. Requires the original bid request's `config.image_enabled=true` — otherwise respond with `placement="text"`.
    </Note>

    <ResponseField name="ad_format" type="string" default="sponsored_message">
      `"sponsored_message"`. Optional — this is the default when omitted.
    </ResponseField>

    <ResponseField name="placement" type="string" required>
      Must be `"image"`. Declares that this creative renders with a hero product image.
    </ResponseField>

    <ResponseField name="headline" type="string" required>
      Ad headline text. Must respect the `config.max_headline_chars` value sent on the bid request.
    </ResponseField>

    <ResponseField name="description" type="string">
      Ad body text/description. Optional.
    </ResponseField>

    <ResponseField name="url" type="string" required>
      Click-through URL. This is your own click tracking URL (your domain, your format). The SSP passes it through to the publisher as-is.
    </ResponseField>

    <ResponseField name="advertiser" type="string" required>
      Brand display name (e.g. `"Nike"`).
    </ResponseField>

    <ResponseField name="domain" type="string">
      Brand domain (e.g. `"nike.com"`). Optional.
    </ResponseField>

    <ResponseField name="cta_text" type="string" required>
      Call-to-action text (e.g. `"Learn more"`, `"Shop Now"`).
    </ResponseField>

    <ResponseField name="image_url" type="string" required>
      Hero/product image URL — rendered as the main visual of the card. **Required for `placement="image"`.**
    </ResponseField>

    <ResponseField name="logo_url" type="string">
      Advertiser brand mark. Optional, but recommended — publishers may render it as a small logo next to the product image.
    </ResponseField>

    <ResponseField name="view_url" type="string">
      Impression tracking URL. If provided, the SSP appends it as a query parameter to the click URL; the publisher fires it on render. Optional.
    </ResponseField>

    <ResponseField name="feedback_url" type="string">
      URL where Thrad should forward user feedback (thumbs up / thumbs down) for this ad. Optional — overrides any `feedback_url` you returned in the bid response. See [Feedback Passthrough](/dsp/api-reference/dsp-feedback-webhook).
    </ResponseField>
  </Expandable>

  <Expandable title="data properties (sponsored_message, placement=text)">
    <Note>
      Compact card with advertiser logo + copy, no hero image. Use when you don't have a real product photo — brand awareness creatives, or catalogues that only have logo-style assets. Always valid regardless of `config.image_enabled`.
    </Note>

    <ResponseField name="ad_format" type="string" default="sponsored_message">
      `"sponsored_message"`. Optional — this is the default when omitted.
    </ResponseField>

    <ResponseField name="placement" type="string" required>
      Must be `"text"`. Declares that this creative renders compactly with a logo rather than a hero image.
    </ResponseField>

    <ResponseField name="headline" type="string" required>
      Ad headline text. Must respect the `config.max_headline_chars` value sent on the bid request.
    </ResponseField>

    <ResponseField name="description" type="string">
      Ad body text/description. Optional.
    </ResponseField>

    <ResponseField name="url" type="string" required>
      Click-through URL. This is your own click tracking URL (your domain, your format). The SSP passes it through to the publisher as-is.
    </ResponseField>

    <ResponseField name="advertiser" type="string" required>
      Brand display name (e.g. `"Acme Sports"`).
    </ResponseField>

    <ResponseField name="domain" type="string">
      Brand domain (e.g. `"acmesports.com"`). Optional.
    </ResponseField>

    <ResponseField name="cta_text" type="string" required>
      Call-to-action text (e.g. `"Learn more"`, `"Shop Now"`).
    </ResponseField>

    <ResponseField name="logo_url" type="string" required>
      Advertiser brand/logo URL — rendered as the card's visual. **Required for `placement="text"`.**
    </ResponseField>

    <ResponseField name="image_url" type="string">
      Ignored for `placement="text"`. Send as `null` or omit.
    </ResponseField>

    <ResponseField name="view_url" type="string">
      Impression tracking URL. If provided, the SSP appends it as a query parameter to the click URL; the publisher fires it on render. Optional.
    </ResponseField>

    <ResponseField name="feedback_url" type="string">
      URL where Thrad should forward user feedback (thumbs up / thumbs down) for this ad. Optional — overrides any `feedback_url` you returned in the bid response. See [Feedback Passthrough](/dsp/api-reference/dsp-feedback-webhook).
    </ResponseField>
  </Expandable>

  <Expandable title="data properties (carousel)">
    <Note>
      **Beta.** Carousel is in early release. Upcoming improvements: support for mixed-brand carousels (multiple advertisers per carousel) and per-tile viewability tracking (which tile was actually seen vs only rendered). Current constraints — same-brand only, single carousel-level `view_url` — will be relaxed in a future version.
    </Note>

    <ResponseField name="ad_format" type="string" required>
      Must be `"sponsored_carousel"`. Required for carousel responses (no default).
    </ResponseField>

    <ResponseField name="advertiser" type="string">
      Brand display name shared across all tiles. Optional but recommended — **all tiles in a carousel must belong to the same brand**.
    </ResponseField>

    <ResponseField name="domain" type="string">
      Brand domain shared across all tiles. Optional.
    </ResponseField>

    <ResponseField name="logo_url" type="string">
      Shared brand mark/logo URL covering the whole carousel. Optional.
    </ResponseField>

    <ResponseField name="items" type="array" required>
      Carousel tiles. **Exactly 3 items required, all from the same brand.** Order is preserved (index 0 is the lead tile). Cross-brand carousels are rejected.

      <Expandable title="item properties">
        <ResponseField name="headline" type="string" required>
          Tile headline. Must respect `config.max_headline_chars` from the original bid request.
        </ResponseField>

        <ResponseField name="image_url" type="string" required>
          Tile image URL. **Required for every tile** — carousel without images is rejected.
        </ResponseField>

        <ResponseField name="url" type="string" required>
          Per-tile click-through URL. Must be unique per tile so click attribution can identify which tile was clicked.
        </ResponseField>

        <ResponseField name="description" type="string">
          Optional secondary text for the tile.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="view_url" type="string">
      Single impression/view tracking pixel URL covering the whole carousel. Optional.
    </ResponseField>

    <ResponseField name="feedback_url" type="string">
      URL where Thrad should forward user feedback for this carousel. Optional — overrides any `feedback_url` from the bid response. See [Feedback Passthrough](/dsp/api-reference/dsp-feedback-webhook).
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="error" type="string">
  Error message when the request fails. Only present on error responses.
</ResponseField>

<Warning>
  **No extra fields allowed**: The response model uses strict validation (`extra="forbid"`). If your response includes **any field not listed above**, validation will fail and the ad will be dropped. For example, adding `"custom_tracking": "something"` to the `data` object will cause the entire render response to be rejected.
</Warning>

<Note>
  **Click URL requirement**: We append a `view_url` parameter to the click URL for impression verification. Your click tracker must pass through or ignore unknown query parameters.
</Note>

## Status Codes

| Status Code                 | Meaning          | Scenario                                                               |
| --------------------------- | ---------------- | ---------------------------------------------------------------------- |
| `200 OK`                    | Success          | Ad creative generated successfully                                     |
| `400 Bad Request`           | Validation error | Missing required fields (`description` or `url`) or invalid data types |
| `404 Not Found`             | Bid not found    | Bid expired (TTL \~60s), invalid bidId, or bid not in cache            |
| `500 Internal Server Error` | Server error     | LLM service unavailable, creative generation failed, or database error |
