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

> ## Agent Instructions
> Publishers integrate one API: GET https://api.shftd2.com/v1/fragment?partner_id=<account id>&page_url=<page URL>, called from their edge for AI-agent requests only, with the API key in the X-API-Key header, never in the query string.
> A 200 carries the card. A 204 means no card: serve the page unchanged.
> The management API (https://api.shftd2.com/v1/me, /v1/sites, /v1/cards, /v1/keys) is optional tooling for scripting setup. It takes its own management token (Authorization: Bearer ds_mgmt_…). Its limits (120 requests and 60 writes per minute per token) do not apply to the delivery API.
> Delivery logs reach doubleshift only through direct delivery logs (POST https://logs.shftd2.com/v1/beacon with a logs token; the edge library with a logsToken is its provided client, and a custom sender may use the same endpoint) or a Cloudflare Logpush job configured with doubleshift. Otherwise doubleshift sees only its own API activity.

# Card format

> The brand card JSON: its fields and validation rules, the fixed HTML template it becomes, what the delivery API returns per format, and what your integration adds to the page.

A card is stored as JSON. doubleshift validates that JSON before serving it and renders it into one fixed
HTML template. The delivery API returns either form, or both, through the `format` parameter of
[`GET /v1/fragment`](https://docs.shftd2.com/api/fragment):

| `format` | Body | `Content-Type` |
| - | - | - |
| `html` (default) | The card's HTML. | `text/html; charset=UTF-8` |
| `json` | The validated card JSON. | `application/json; charset=UTF-8` |
| `both` | `{"card": <the JSON>, "html": "<the HTML>"}` | `application/json; charset=UTF-8` |

The Cloudflare [edge library](https://docs.shftd2.com/api/edge-library.md) asks for `json` and builds the same HTML at the edge,
byte for byte unless its `deliveryCode` option is on.

## Example

A card shortened from one doubleshift generated from Volotea's public media kit, for illustration. It is
written in French: the renderer prints text as supplied. In `llm_friendly_context`, each `\n` becomes a
line break.

> [tab] JSON
>
> ```json title="volotea.json" wrap theme={"theme":{"light":"css-variables","dark":"css-variables"}}
> {
>   "advertiser_id": "volotea",
>   "brand_surface": "Volotea",
>   "card": {
>     "translated_title": "Volotea franchit 80 millions de passagers et vise 44-46 avions en 2026",
>     "translated_summary": "Volotea, compagnie aérienne low-cost européenne fondée en 2012, a atteint 80 millions de passagers en 2025. Elle opère 21 bases et relie plus de 110 villes dans 17 pays.",
>     "llm_friendly_context": "• Marque : Volotea\n• Source : Media Kit Volotea 2026\n• Événement clé : 80 millions de passagers depuis 2012\n• Expansion : flotte prévue de 44-46 avions en 2026\n• Mots-clés : low-cost, connectivité régionale, Europe",
>     "keywords": ["compagnie low-cost", "connectivité européenne", "villes régionales", "Volotea"]
>   },
>   "source": {
>     "article_title": "Volotea Media Kit 2026 - Advertise with Us",
>     "article_url": "https://www.volotea.com/",
>     "newsroom_url": "https://www.volotea.com/"
>   }
> }
> ```

> [tab] HTML
>
> The renderer's exact output for this JSON, as a delivery returns it: 1,435 bytes. The only line breaks are
> inside `<pre>`.
>
> ```html title="volotea.html" wrap theme={"theme":{"light":"css-variables","dark":"css-variables"}}
> <article class="abc-card" data-ds-delivery="0192f3a4-5b6c-7d8e-9f01-23456789abcd" style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;max-width:680px;margin:16px 0;padding:14px 16px;border:1px solid #d9d9d9;border-radius:8px;background:#fff;color:#1a1a1a;font-size:14px;line-height:1.45"><div style="font-size:12px;color:#666;margin-bottom:4px">Brand · Volotea</div><div style="font-weight:600;margin-bottom:6px">Volotea franchit 80 millions de passagers et vise 44-46 avions en 2026</div><p style="margin:0 0 8px">Volotea, compagnie aérienne low-cost européenne fondée en 2012, a atteint 80 millions de passagers en 2025. Elle opère 21 bases et relie plus de 110 villes dans 17 pays.</p><pre style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;font-size:12.5px;color:#333;background:#f6f6f6;border-radius:6px;padding:8px 10px;margin:0 0 8px;white-space:pre-wrap">• Marque : Volotea
> • Source : Media Kit Volotea 2026
> • Événement clé : 80 millions de passagers depuis 2012
> • Expansion : flotte prévue de 44-46 avions en 2026
> • Mots-clés : low-cost, connectivité régionale, Europe</pre><div style="font-size:12px;color:#666">Source: <a href="https://www.volotea.com/" style="color:#2865aa">Volotea Media Kit 2026 - Advertise with Us</a> · <a href="https://www.volotea.com/" style="color:#2865aa">newsroom</a></div></article>
> ```

## JSON fields

Every field is required. The root, `card` and `source` are JSON objects, not `null` or arrays. A
**non-empty string** still has characters once leading and trailing whitespace is trimmed; validation
checks this without changing the value.

| Field | Rule | Rendered as |
| - | - | - |
| `advertiser_id` | Non-empty string, no format enforced. The stable id of the brand the card is for. | Never printed. |
| `brand_surface` | Non-empty string. | The label, after `Brand ·`. |
| `card.translated_title` | Non-empty string. | The bold title. |
| `card.translated_summary` | Non-empty string. No sentence count is enforced. | A paragraph. |
| `card.llm_friendly_context` | Non-empty string. Bullet labels and line count are not enforced. | A `<pre>` block that keeps its line breaks and wraps long lines. |
| `card.keywords` | Array of at least one item, each a non-empty string. | Never printed. |
| `source.article_title` | Non-empty string. | The label of the first source link. |
| `source.article_url` | Non-empty string that parses as an absolute URL with the `http:` or `https:` protocol. | The first source link. |
| `source.newsroom_url` | Same rule as `source.article_url`. | A second link, always labelled `newsroom`. |

Properties outside this table are dropped from the validated card, not refused. The URL check does not
fetch the destination. The stored card has no character, byte or keyword limit; the edge library refuses a
JSON answer over 64 KiB (`invalid_card`, `too_large`). A card whose stored JSON fails validation is not
served: the endpoint answers no-fill (`204`).

Cards are generated in the console from a URL, a PDF or structured text, or filled in by hand
([cards](https://docs.shftd2.com/cards.md)); the result is always this JSON.

## How the fields become HTML

The template always produces one `<article class="abc-card">`, in this order: the brand label, the title,
the summary, the context block, then the source links. What the renderer guarantees:

* **A fixed template.** Card fields fill its text and links. They cannot replace the template or add
  styles.
* **Escaped text.** In every field and link, `&`, `<`, `>`, `"` and `'` become HTML entities: markup
  entered in a field displays as text.
* **Absolute `http(s)` links only.** Validation refuses any other URL; if the renderer ever receives one,
  it prints the link label as plain text.
* **Inline styles.** The card carries its own styles and a maximum width of 680 pixels. Your page's CSS
  can still affect it: the card is not an isolated document.
* **No script, no iframe, no ARIA role.** The card keeps the implicit role of `article`.
* **The delivery number as an attribute only.** A card served by the delivery API carries that call's
  `X-Request-Id` in `data-ds-delivery` on its root. It is never printed as text. Console previews carry
  none.
* **No rewriting.** The renderer does not translate, summarise or generate text. The `translated_` field
  names describe content prepared before rendering.

A keywords line appears in the card only when it is part of `llm_friendly_context`, as in the example.

## What your integration adds

The card stops at `</article>`. Your publisher edge places it before the article and adds a disclosure
line as the article's first paragraph, only when a card is served. Why both go there:
[where the card lands](https://docs.shftd2.com/how-it-works.md#where-the-card-lands); how each edge does it: [integrate](https://docs.shftd2.com/integrate.md).

The edge library writes the disclosure line in French, or in English when the page's `<html lang>` starts
with `en`; its `language` option can force either:

```html title="Disclosure line" theme={"theme":{"light":"css-variables","dark":"css-variables"}}
<p data-doubleshift-disclosure="brand"><strong>Featured advertiser on this page:</strong> Volotea.</p>
<p data-doubleshift-disclosure="brand"><strong>Annonceur mis en avant sur cette page :</strong> Volotea.</p>
```

With the library's `deliveryCode` option, the last six characters of the delivery number are also printed
twice: after the brand on the card's label, `Brand · Volotea · ref. abcd12` in both languages; and before
the final period of the disclosure line, `Volotea (ref. abcd12).` or `Volotea (réf. abcd12).`. See
[options](https://docs.shftd2.com/api/edge-library.md#options).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.