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

# Concepts

> Bundles, the store API key, caching, and the never-throw contract.

## Bundles

The SDK works with two bundle shapes, both plain JSON returned by the AgentShop SEO endpoint:

**`SeoBundle`** — returned for products and collections. Contains everything a page needs:

```ts theme={null}
{
  jsonLd: unknown;        // schema.org JSON-LD graph (plain object)
  title: string;
  description: string;
  canonical: string;
  openGraph: SeoOpenGraph; // og:type is "product" for products, "website" for collections
  twitter: SeoTwitter;
}
```

**`SeoSiteBundle`** — returned by the site endpoint. Contains only `{ jsonLd }`: the site-level `Organization` + `WebSite` (+ `FAQPage`) graph. It deliberately has no title/meta — your home page owns its own meta tags.

See the [core API reference](/sdk/reference/core) for the full type shapes.

## The store API key

Every request is authenticated by your store **API key** (`ask_…`), sent as `Authorization: Bearer`. Generate and rotate it in the dashboard under **Settings → API Keys**.

* It's a **secret** — server-side only, never in a client bundle or the browser. See [Security](/sdk/security).
* It resolves to **exactly one store**, so there's no store id in the endpoint path. A merchant with several connected stores gets one key per store.
* **Rotation** is safe: after you rotate, the previous key keeps working for 24 hours so you can redeploy without downtime.

## Caching

Because each response is scoped to a store by a secret key, the endpoint marks it **private** — it can't be stored in a shared or CDN cache. Your framework's own cache carries the load instead:

1. **Endpoint headers**: `Cache-Control: private, max-age=3600, stale-while-revalidate=86400`, plus an `X-Cache: HIT|MISS` header telling you whether the backend served the bundle from its own cache.
2. **Framework cache** (the primary layer): the Next.js helpers wrap every fetch with `{ next: { revalidate: 3600 } }` (ISR, 1 hour) by default, so a page's bundle is fetched once per revalidation window, not per request.

Override the framework layer via `fetchOptions`:

```ts theme={null}
createProductMetadata({ fetchOptions: { next: { revalidate: 60 } } });
```

Override the framework layer via `fetchOptions`:

```ts theme={null}
createProductMetadata({ fetchOptions: { next: { revalidate: 60 } } });
```

## Graceful degradation (the never-throw contract)

Every fetch helper returns `null` on **any** failure — missing configuration, a missing or invalid key (401), 404, timeout, network error. Components render nothing, metadata helpers return an empty object, and your page ships without SEO tags rather than crashing.

* A missing base URL logs a single `console.warn` (once per process) and skips tags.
* A misconfigured store renders perfectly fine — pages return 200, just without SEO tags. The only exception is the sitemap route, which returns a 502 with an empty body when the bundle is unavailable (a sitemap must be real XML or an error).
* If you want loud failures instead — for example in a health check — call `resolveConfig(cfg)`, the throwing validation variant.

## Where each artifact goes

| Artifact                  | Where                               | Helper                                            |
| ------------------------- | ----------------------------------- | ------------------------------------------------- |
| Product meta + JSON-LD    | `app/products/[handle]/page.tsx`    | `createProductMetadata` + `<ProductJsonLd>`       |
| Collection meta + JSON-LD | `app/collections/[handle]/page.tsx` | `createCollectionMetadata` + `<CollectionJsonLd>` |
| Site-level JSON-LD        | Home page **only** (`app/page.tsx`) | `<SiteJsonLd>`                                    |
| Sitemap                   | `app/sitemap.xml/route.ts`          | `productSitemapRoute`                             |

<Warning>
  Inject `<SiteJsonLd>` exactly once, on the home page. Google discourages repeating the same `FAQPage`/site graph across many pages.
</Warning>
