SegOps AIDocs

AI Pages API

Base path: `/api/ai-pages/`

Authentication: JWT Bearer token or ApiKey sk_<raw> header. All endpoints require IsAuthenticated unless noted.


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:

ParamTypeRequiredDescription
qstringNatural language search query
limitintMax results (default 20, max 100)
segment_idintConstrain to products in this product segment

Response:

json
{
  "query": "running shoes under $150",
  "products": [
    {
      "id": 42,
      "sku": "RUN-001",
      "name": "UltraRun Pro",
      "data": { "brand": "Nike", "price": 129.99, "category": "running" },
      "relevance_score": 0.8923
    }
  ],
  "filters": {
    "brand": ["Nike", "Adidas", "Brooks"],
    "category": ["running", "trail"]
  },
  "metadata": {
    "match_count": 12,
    "latency_ms": 87
  }
}

Errors:

StatusReason
400q parameter missing
429Rate limit exceeded
503OpenAI 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:

FieldTypeRequiredDescription
slugstringURL slug (unique per tenant)
queryintAIQuery ID to base content on
titlestringPage title (defaults to query text)
publishedboolDefault 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)#

POST /api/ai-pages/pages/<id>/build/

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:

FieldTypeRequiredDescription
messagestringyesNatural-language instruction for the agent

SSE event types:

data: {"type": "thinking",  "content": "Fetching products for running shoes..."}
data: {"type": "block",     "block": {"id": "b1", "type": "hero", "props": {"headline": "..."}}}
data: {"type": "text",      "content": "Added a hero block with your top product."}
data: {"type": "error",     "content": "Could not fetch products: feed not found"}
data: {"type": "done"}

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:

StatusReason
403Page limit reached (WithinPageLimit gate)
404Page not found or belongs to another tenant

Design system#

Get design system

GET /api/ai-pages/design-system/

Returns the active design system for the authenticated tenant. Creates a default instance if none exists.

Response:

json
{
  "id": 1,
  "tenant": 7,
  "primary_color": "#1E40AF",
  "secondary_color": "#64748B",
  "background_color": "#FFFFFF",
  "text_color": "#0F172A",
  "heading_font": "Inter",
  "body_font": "Inter",
  "border_radius": "8px",
  "spacing_unit": 4,
  "custom_css": "",
  "updated_at": "2026-05-10T12:00:00Z"
}

Update design system

PUT /api/ai-pages/design-system/

All fields are optional; unset fields retain their current values.

Request body:

FieldTypeDescription
primary_colorstringHex color value
secondary_colorstringHex color value
background_colorstringHex color value
text_colorstringHex color value
heading_fontstringFont family name
body_fontstringFont family name
border_radiusstringCSS value (e.g. "8px")
spacing_unitintegerBase spacing in px
custom_cssstringFree-form CSS appended after tokens

Response: Updated design system object (same shape as GET).

Extract design system from image

POST /api/ai-pages/design-system/extract/

Streams a Claude vision extraction of brand colors and fonts from an image. Results are suggestions only — they are not automatically saved.

Request body:

FieldTypeRequiredDescription
image_urlstringyesURL of a screenshot, brand asset, or homepage

SSE event types:

data: {"type": "extracted", "field": "primary_color",   "value": "#1E40AF"}
data: {"type": "extracted", "field": "secondary_color", "value": "#64748B"}
data: {"type": "extracted", "field": "heading_font",    "value": "Inter"}
data: {"type": "extracted", "field": "body_font",       "value": "Inter"}
data: {"type": "note",      "content": "Detected a clean sans-serif design system."}
data: {"type": "done"}

Page templates#

List templates

GET /api/ai-pages/templates/

Returns all page templates for the authenticated tenant.

Response:

json
[
  {
    "id": 3,
    "name": "Product Launch",
    "description": "Hero + product grid + CTA banner",
    "blocks": [
      {"type": "hero",         "props": {}},
      {"type": "product_grid", "props": {"columns": 3}},
      {"type": "cta_banner",   "props": {}}
    ],
    "tags": ["launch", "product"],
    "created_at": "2026-05-10T09:00:00Z"
  }
]

Create template

POST /api/ai-pages/templates/

Request body:

FieldTypeRequiredDescription
namestringyesTemplate display name
descriptionstringnoShort description
blocksarrayyesOrdered array of {type, props} objects
tagsstring[]noFree-form tags

Response: 201 Created — the created template object.

Get / Update / Delete template

GET    /api/ai-pages/templates/<id>/
PATCH  /api/ai-pages/templates/<id>/
DELETE /api/ai-pages/templates/<id>/

External product feeds#

List feeds

GET /api/ai-pages/feeds/

Response:

json
[
  {
    "id": 1,
    "name": "Partner Catalog",
    "url": "https://partner.example.com/feed.json",
    "format": "json",
    "field_map": {"product_name": "name", "item_sku": "sku"},
    "last_synced_at": "2026-05-10T08:00:00Z",
    "created_at": "2026-05-09T12:00:00Z"
  }
]

Create feed

POST /api/ai-pages/feeds/

Request body:

FieldTypeRequiredDescription
namestringyesDisplay name
urlstringyesFeed URL
formatstringyesjson or xml
field_mapobjectnoSource field → canonical field mapping

Response: 201 Created — the created feed object.

Get / Update / Delete feed

GET    /api/ai-pages/feeds/<id>/
PATCH  /api/ai-pages/feeds/<id>/
DELETE /api/ai-pages/feeds/<id>/

Sync feed

POST /api/ai-pages/feeds/<id>/sync/

Enqueues a fetch_external_feed Celery task to fetch and upsert products from the feed.

Response: 202 Accepted

json
{ "status": "queued" }

Page export#

Request export

POST /api/ai-pages/pages/<id>/export/

Queues an async export_page Celery task. Tracking snippet is auto-injected for tsx and html formats.

Request body:

FieldTypeRequiredDescription
formatstringyestsx, html, json, or zip

Response: 202 Accepted

json
{ "export_id": 15, "status": "pending" }

Get export status / download

GET /api/ai-pages/pages/<id>/export/

Response:

json
{
  "id": 15,
  "format": "html",
  "status": "ready",
  "download_url": "https://storage.example.com/exports/page-42.html?sig=...",
  "created_at": "2026-05-10T13:00:00Z",
  "expires_at": "2026-05-11T13:00:00Z"
}

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

GET /api/ai-pages/jobs/

Returns all bulk generation jobs for the authenticated tenant, most recent first.

Response:

json
[
  {
    "id": 8,
    "status": "running",
    "total": 24,
    "completed": 12,
    "failed": 1,
    "created_at": "2026-05-10T14:00:00Z",
    "finished_at": null
  }
]

status values: pending, running, complete, partial_failure

Get bulk job detail

GET /api/ai-pages/jobs/<id>/

Returns the job summary plus a per-page breakdown:

json
{
  "id": 8,
  "status": "complete",
  "total": 24,
  "completed": 23,
  "failed": 1,
  "pages": [
    { "page_id": 101, "slug": "running-shoes-under-150", "status": "ready" },
    { "page_id": 102, "slug": "trail-running-gear",      "status": "error", "error": "Embedding service timeout" }
  ],
  "created_at": "2026-05-10T14:00:00Z",
  "finished_at": "2026-05-10T14:05:22Z"
}

Block registry#

GET /api/ai-pages/blocks/

Returns the canonical list of available block types with their schema.

Auth: Bearer token or API key.

Response:

json
[
  {
    "type": "hero",
    "label": "Hero",
    "description": "Full-width hero with headline, subheadline, CTA, and optional background image",
    "props_schema": {
      "headline":    {"type": "string", "required": true},
      "subheadline": {"type": "string", "required": false},
      "cta_label":   {"type": "string", "required": false},
      "cta_url":     {"type": "string", "required": false},
      "image_url":   {"type": "string", "required": false}
    }
  },
  {
    "type": "product_grid",
    "label": "Product Grid",
    "description": "Grid of product cards with image, name, price, and link",
    "props_schema": {
      "product_skus": {"type": "array",   "required": true},
      "columns":      {"type": "integer", "required": false, "default": 3}
    }
  }
]

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 from postgres:16)
  • OPENAI_API_KEY env var required for embedding generation
  • Embeddings stored in ai_pages_productembedding table with HNSW index
  • Products are embedded automatically via post_save signal on Product model
  • Backfill: apps.pim.tasks.generate_all_embeddings(tenant_id)