> For the complete documentation index, see [llms.txt](https://docs.carpose.de/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.carpose.de/misc-modules/09_server-rendered-inventory.md).

# Inventory Fragment

{% hint style="info" %}
A dealer's inventory as ready-made HTML instead of a JavaScript widget: a run of `<article>` elements your CMS plugin or template drops straight into a page of its own. Public — no API key, no authentication.
{% endhint %}

## When to use it

The [vehicle search modules](/vehicle-search-modules/01_vehicle-search-form.md) render in the browser and are the right choice for almost every site. Reach for the fragment when the markup has to exist in the server response: a CMS plugin producing crawlable listing pages, a static build, or a template that has to work without JavaScript.

It carries the same data as the widget, in HTML you own and style yourself.

## The endpoint

```
GET /inventory/{companySlug}/fragment?mode=index&limit=50&offset=0
GET /inventory/{companySlug}/fragment?mode=detail&vehicle={slug}
```

`{companySlug}` is the dealer's Carposé slug — the same one the vehicle sitemap is published under.

### Response

```
Content-Type:     text/html; charset=UTF-8
Cache-Control:    public, max-age=3600
X-Carpose-Total:  1109
X-Robots-Tag:     noindex
```

The body is a run of `<article>` elements and nothing around them — no doctype, no `<html>`, `<head>`, `<body>` or `<footer>`, no `<style>` block, no `class` attributes, and no template indentation. Drop it into your own container and style it with your own selectors.

`X-Carpose-Total` is the dealer's whole active inventory, not the number of articles in this response. Page count, the boundary where your listing should answer 404, and a meta description that names a number all come from this header rather than from counting rows.

{% hint style="info" %}
`X-Robots-Tag: noindex` applies to the fragment URL itself, never to your page. It stops the raw endpoint from being indexed instead of your listing; the markup you render is yours and is indexed normally.
{% endhint %}

### Query parameters

| Parameter | Type                | Default | Description                                                                |
| --------- | ------------------- | ------- | -------------------------------------------------------------------------- |
| `mode`    | `index` \| `detail` | `index` | A page of the inventory, or one vehicle in full. Any other value is a 400. |
| `limit`   | integer             | `50`    | Articles per response, clamped to 1…500.                                   |
| `offset`  | integer             | `0`     | How many articles to skip. Negative values are read as `0`.                |
| `vehicle` | string              | —       | Required for `mode=detail`; missing is a 400, unknown is a 404.            |

`limit` and `offset` page over the **whole** inventory, in one order spanning every location. You never have to map a page number onto a location.

### What the two modes carry

|                       | `mode=index`        | `mode=detail`       |
| --------------------- | ------------------- | ------------------- |
| Images                | the lead image only | the whole gallery   |
| Equipment description | omitted             | included, sanitized |
| JSON-LD `image`       | the lead image only | every image         |

Images arrive in gallery order — `_1` before `_10`, numerically.

### Addressing a vehicle

`vehicle` accepts every form a deep link can carry, so you can hand back whatever your listing already holds:

```
?vehicle=audi-q3-s-line--efbb7d07-0101-11f0-8de6-960000658564   the canonical token
?vehicle=efbb7d07-0101-11f0-8de6-960000658564                   the UUID alone
?vehicle=audi-q3-s-line                                         the slug alone
```

All three resolve to the same article. The token is also the article's `id`, so a listing can hand its own anchor straight back as the detail request. See [URL Structure](/misc-modules/08_url-structure.md) for how the token is formed and when an account is set to slug-only addressing.

## Links inside the fragment

Every article already points where it should. The `<a>` in the article and the `url` in the JSON-LD both carry the dealer's own vehicle URL, built from their account settings — URL form, slug-only, base page. There is nothing to rewrite on your side.

{% hint style="warning" %}
**The account needs a vehicle-search page URL.** Without it Carposé does not know where the dealer's vehicles live, so articles come back with no link and the JSON-LD omits `url` rather than pointing somewhere wrong. Set the search page URL on the account before you integrate.
{% endhint %}

## Elements you will see

`article` `h2` `h3` `p` `div` `span` `a` `img` `dl` `dt` `dd` `ul` `ol` `li` `strong` `em` `br`, plus one `script[type="application/ld+json"]` per article.

Shape of one article:

```html
<article id="{slug}--{uuid}">
  <h2>Vehicle title</h2>
  <div><a href="https://dealer.example/fahrzeugsuche/?vehicle=…">Zum Fahrzeug</a></div>
  <div><img src="…" alt="Vehicle title" loading="lazy"></div>
  <dl><dt>Erstzulassung</dt><dd>2020-02-01</dd>…</dl>
  <div><!-- equipment description, mode=detail only --></div>
  <script type="application/ld+json">{"@context":"https://schema.org","@type":"Car",…}</script>
</article>
```

{% hint style="warning" %}
**Sanitize it anyway.** The equipment description is dealer-authored text imported from mobile.de, and Carposé already restricts it to the elements listed above. Running your own allowlist over the response before you print it is still the right call — it is the only check that survives a change on our side, and it costs a single pass.
{% endhint %}

## Caching

Responses are cached for an hour and rebuilt the moment the dealer's inventory or link settings change, so a fresh import shows up without waiting out the TTL. `Cache-Control: public, max-age=3600` is safe to honour in your own cache or CDN.

## Errors

| Status | Cause                                                                        |
| ------ | ---------------------------------------------------------------------------- |
| 400    | `mode` is neither `index` nor `detail`, or `mode=detail` without `vehicle`   |
| 404    | unknown company slug, or `vehicle` names a vehicle this dealer does not have |

An offset past the end of the inventory is not an error — it answers 200 with an empty body. Compare `offset` against `X-Carpose-Total` to decide where your own listing stops.
