AI Pages API
Base path: `/api/ai-pages/`
Authentication: JWT Bearer token or ApiKey sk_<raw> header. All endpoints require IsAuthenticated unless noted.
Query-to-Product Search#
GET /api/ai-pages/query#
Semantic product search via pgvector cosine similarity. Embeds the query text with OpenAI text-embedding-3-small and returns the closest matching products.
Rate limit: 100 requests/minute per tenant.
Query parameters:
| Param | Type | Required | Description |
|---|---|---|---|
q | string | ✅ | Natural language search query |
limit | int | — | Max results (default 20, max 100) |
segment_id | int | — | Constrain to products in this product segment |
Response:
Errors:
| Status | Reason |
|---|---|
| 400 | q parameter missing |
| 429 | Rate limit exceeded |
| 503 | OpenAI embedding service unavailable |
Landing Pages (M19b)#
GET /api/ai-pages/pages/#
List all generated pages for the authenticated tenant.
Response: Array of GeneratedPage objects (lightweight — no intro_copy or product_skus).
POST /api/ai-pages/pages/#
Create a new generated page.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
slug | string | ✅ | URL slug (unique per tenant) |
query | int | — | AIQuery ID to base content on |
title | string | — | Page title (defaults to query text) |
published | bool | — | Default false |
PATCH /api/ai-pages/pages/<id>/#
Update slug, title, or published status.
DELETE /api/ai-pages/pages/<id>/#
Delete a page.
POST /api/ai-pages/pages/<id>/regenerate/#
Queue a generate_page Celery task to re-run semantic search and AI copy generation.
Response: { "status": "queued" } (202)
POST /api/ai-pages/pages/bulk-generate/#
Create + queue pages for all active AIQueries in the tenant's account.
Response: { "job_id": <id>, "status": "queued" } (202)
GET /api/ai-pages/public/pages/<tenant_slug>/<page_slug>/#
Auth: None — public endpoint.
Returns full page data including intro_copy and product_skus. Increments view_count. Sets last_ai_crawl_at if User-Agent matches GPTBot, ClaudeBot, PerplexityBot, GoogleOther, or anthropic-ai.
Response: Full GeneratedPage serializer output.
Visual Page Builder (M20)#
Build a page (streaming)#
Streams a LangGraph ReAct agent session as Server-Sent Events. The agent interprets the message body and performs block operations on the page.
Auth: Bearer token or API key. Page must belong to the authenticated tenant.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
message | string | yes | Natural-language instruction for the agent |
SSE event types:
block events represent operations the agent applied to the page (emit or update). The full updated blocks array is available via GET /api/ai-pages/pages/<id>/ after the stream completes.
Errors:
| Status | Reason |
|---|---|
| 403 | Page limit reached (WithinPageLimit gate) |
| 404 | Page not found or belongs to another tenant |
Design system#
Get design system
Returns the active design system for the authenticated tenant. Creates a default instance if none exists.
Response:
Update design system
All fields are optional; unset fields retain their current values.
Request body:
| Field | Type | Description |
|---|---|---|
primary_color | string | Hex color value |
secondary_color | string | Hex color value |
background_color | string | Hex color value |
text_color | string | Hex color value |
heading_font | string | Font family name |
body_font | string | Font family name |
border_radius | string | CSS value (e.g. "8px") |
spacing_unit | integer | Base spacing in px |
custom_css | string | Free-form CSS appended after tokens |
Response: Updated design system object (same shape as GET).
Extract design system from image
Streams a Claude vision extraction of brand colors and fonts from an image. Results are suggestions only — they are not automatically saved.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
image_url | string | yes | URL of a screenshot, brand asset, or homepage |
SSE event types:
Page templates#
List templates
Returns all page templates for the authenticated tenant.
Response:
Create template
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Template display name |
description | string | no | Short description |
blocks | array | yes | Ordered array of {type, props} objects |
tags | string[] | no | Free-form tags |
Response: 201 Created — the created template object.
Get / Update / Delete template
External product feeds#
List feeds
Response:
Create feed
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Display name |
url | string | yes | Feed URL |
format | string | yes | json or xml |
field_map | object | no | Source field → canonical field mapping |
Response: 201 Created — the created feed object.
Get / Update / Delete feed
Sync feed
Enqueues a fetch_external_feed Celery task to fetch and upsert products from the feed.
Response: 202 Accepted
Page export#
Request export
Queues an async export_page Celery task. Tracking snippet is auto-injected for tsx and html formats.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
format | string | yes | tsx, html, json, or zip |
Response: 202 Accepted
Get export status / download
Response:
status values: queued, building, ready, error
download_url is a signed GCS URL, valid for 24 hours. Present only when status is ready.
Bulk job tracking#
List bulk jobs
Returns all bulk generation jobs for the authenticated tenant, most recent first.
Response:
status values: pending, running, complete, partial_failure
Get bulk job detail
Returns the job summary plus a per-page breakdown:
Block registry#
Returns the canonical list of available block types with their schema.
Auth: Bearer token or API key.
Response:
All 6 block types are returned: hero, product_grid, text_section, cta_banner, feature_list, testimonial.
Infrastructure Notes#
- Postgres image must be
pgvector/pgvector:pg16(changed frompostgres:16) OPENAI_API_KEYenv var required for embedding generation- Embeddings stored in
ai_pages_productembeddingtable with HNSW index - Products are embedded automatically via
post_savesignal onProductmodel - Backfill:
apps.pim.tasks.generate_all_embeddings(tenant_id)