# URL redirects — 404-path lookup

Exact-match redirect lookup (seo-listings card 21). **Hot-path rule: call
this ONLY from the storefront's not-found handler** — never on regular page
loads (index-covered exact match on `(client_id, from_path)`). When
`to_path` is non-null, issue a **301** to it; when null, render the 404.

Redirects are created in admin and **automatically on handle renames** of
products/collections/pages/posts — wiring this once means renames never
break old URLs (path convention shared with [content.md](content.md) and
[menus.md](menus.md)).

## GET /api/store/url-redirects?path=… — lookup

- **Purpose**: resolve a missed pathname (e.g. `/products/old-handle`) to
  its redirect target, if any.
- **Auth**: anon (`x-client-id`).
- **Request**: query `{path}` — the exact pathname that 404'd (leading
  slash included), min length 1.
- **Response**:

```jsonc
{
  "path": "/products/old-handle",   // echoed
  "to_path": "/products/new-handle" // or null — no redirect, render the 404
}
```

- **Errors**: `400 validation_failed` (missing/empty `path`) ·
  `400 missing_client_id`.
- **SDK**: `redirects.lookupRedirect(client, path)`
- **Components**: the storefront `not-found` handler (no visual component).
- **Settings**: admin → Content → URL redirects; automatic rows on handle
  renames.

```bash
# Miss → to_path null (the everyday 404 case), echo intact.
BODY=$(curl -sf "$BASE/api/store/url-redirects?path=/doc-no-such-path-$RUN" \
  -H "x-client-id: $CLIENT_ID")
echo "$BODY" | grep -q '"to_path":null'
echo "$BODY" | grep -q "doc-no-such-path-$RUN"
# Missing path param → 400.
STATUS=$(curl -s -o /dev/null -w '%{http_code}' "$BASE/api/store/url-redirects" \
  -H "x-client-id: $CLIENT_ID")
test "$STATUS" = 400
```

> The hit case (`to_path` non-null) requires an admin-created redirect row;
> it is pinned executably by `tests/store/url-redirects-lookup.test.ts` and
> the SDK contract test `tests/contract/sdk-content.contract.test.ts`.
