Files
descrybe/apps/api/internal/httpapi/v1_openapi.go
T

6712 lines
239 KiB
Go
Raw Normal View History

package httpapi
// OpenAPI 3.0 for the public /api/v1 surface (API-key auth).
// Served at GET /api/v1/openapi.yaml; rendered by the marketing /docs page.
var v1OpenAPIYAML = []byte(`openapi: 3.0.3
info:
title: Descrybe Public API
version: '1.0'
description: |
Public catalog, feed, and product-processing API.
## Base URL
The public API is hosted by Descrybe (customers do not self-host this surface).
- Production: https://descrybe.io/api/v1
OpenAPI document: GET https://descrybe.io/api/v1/openapi.yaml
## Authentication
2026-08-17 21:20:45 +02:00
All /api/v1 operations require a company API key (Bearer or X-API-Key) except
GET /health and GET /openapi.yaml.
Document-level security is BearerAuth OR ApiKeyAuth (same key value).
Do not send dashboard session cookies or CSRF tokens to /api/v1.
### Security schemes (components.securitySchemes)
- BearerAuth - HTTP bearer. Authorization: Bearer dk_your_key (preferred)
- ApiKeyAuth - header X-API-Key: dk_your_key
When both headers are set, Bearer wins. Full key values are never embedded in
this YAML. RapiDoc Try-it: paste a key, or when logged into the docs page use
"Use my API key" (coordinates with the in-app authorize helper).
### Create a key in the app
1. Sign in at https://descrybe.io
2. Open Settings -> API keys (/settings?tab=api-keys)
3. Company admins create a key via dashboard POST /api/api-keys
(session cookie + CSRF; not this public OpenAPI surface). The secret is
shown once and starts with dk_.
4. Store it securely. Later list/revoke shows only key_prefix (first 10
characters). Revoked keys fail auth immediately.
2026-08-16 16:57:36 +02:00
### Cutover / migration (reissue)
API keys from the previous Descrybe platform were not migrated. After
cutover, integrations must create a new dk_ key in Settings -> API keys
(or Use my API key on /docs). Pre-cutover secrets return the same HTTP 401
2026-08-17 21:20:45 +02:00
Unauthorized as unknown keys — there is no separate key-type error.
### 401 Unauthorized
Missing, empty, unknown, revoked, or non-migrated (pre-cutover) keys return
2026-08-17 21:20:45 +02:00
HTTP 401 from RequireAPIKey with the coded envelope:
{ "error": { "code": "unauthorized", "message": "Unauthorized" } }
2026-08-17 21:20:45 +02:00
See components.responses.Unauthorized (schema CodedAPIError). Reissue via
Settings -> API keys (/settings?tab=api-keys).
### 403 Forbidden
Bad API keys on /api/v1 never return 403 (always 401). Tenant scope comes
from the key; cross-company resources typically 404. HTTP 403 appears on
dashboard /api/* session routes (admin required, CSRF mismatch) under
Team/Admin tags (SessionCookie + CSRFHeader) - not this public key surface.
### Rate limits
Heavy mutations are limited to 30 requests per minute per company
(in-process per API replica; not shared across replicas). Counts HTTP
requests, not products inside a bulk body. Limited POST paths:
- /products/process, /process, /process/{id}/retry
- /feeds/{id}/sync, /feeds/{id}/extract-schema, /feeds/{id}/sync-process-sample
- /export-feeds/{id}/generate, /export-feeds/{id}/export-products, /export-feeds/{id}/rotate-token (admin)
Over limit: HTTP 429, header Retry-After: 60, body
{ "error": "rate limit exceeded" } (see TooManyRequests). Ordinary GETs and
other mutations are outside this HTTP budget (process starts may still return
402 for plan/credits).
## Quick curl
curl -s -H "Authorization: Bearer dk_your_key" \
"https://descrybe.io/api/v1/products?page=1&limit=1"
Local Go API (default listen from README):
curl -s -H "Authorization: Bearer dk_your_key" \
"http://localhost:28471/api/v1/products?page=1&limit=1"
Health: GET /api/v1/health (also /healthz and /readyz on the API host).
## Processing contracts (dual-mode)
Two separate surfaces — do not mix bodies or response envelopes:
2026-08-17 21:20:45 +02:00
1. **Public process (source of truth for integrations)**
- POST /products/process with body items[].ean
- GET /products/process/{id}
- Envelope: HTTP 200 { data: { process_id, … } } (and completed items[])
2026-08-17 21:20:45 +02:00
- Matches POST /api/v1/products/process
- Handler also accepts raw_product_ids as an alternate body on this path
- Plan gates (credits / product limit / AI / EPREL / feature flags) run before
EnsureRaw catalog writes on the items[].ean path; blocked starts return HTTP 402
2. **Internal / dashboard-style jobs**
- POST /process with raw_product_ids (same body as POST /api/processing/jobs)
- GET /process, GET /process/{id}, cancel/terminate/retry
- Flat JSON (no data wrapper); 202 Accepted on start/retry
- Plan gates return HTTP 402 with PlanGateError (error + code + upgrade_url)
### Dual IDs (do not confuse)
- GET /products data[].id = processed_products.id (enriched row)
- GET /products data[].raw_product_id = raw_products.id (use this for raw_product_ids)
- POST ... raw_product_ids[] must be raw_products.id — never PresentProduct.id
2026-08-17 09:33:07 +02:00
- GET /products/process/{id} COMPLETED items[] omit internal UUIDs (id /
processed_product_id / raw_product_id). Use GET /products when a UUID is needed.
Display name is items[].name (title omitted when identical).
2026-08-17 21:56:09 +02:00
### AI descriptions (HTML body, not a short blurb)
Default processing_type full runs normalize, parse specs, fill fields, EPREL
(when enabled), categorize, then AI enhance. Enhance writes the display name
and a full product body.
Completed items[].description is that body: one HTML string (tags matching
section types h1/h2/h3/h4, p, ul) — never a JSON array and never a one- or
two-sentence slogan. Length and structure come from the assigned category
description_template (description formula): ordered sections with their own
instructions. A formula can ask for a short paragraph, 100-word blocks,
several heading/list sections, or a very large multi-section article. There
is no request-body size enum; configure length on the category. When a
category has no formula, the default is still 1-3 factual paragraphs with
limited HTML, not a headline-only stub.
items[].description on the start body is optional source text the model may
rewrite. Poll GET /products/process/{id} for the generated HTML.
meta_title / meta_description are separate short SEO fields and may be
omitted depending on company configuration.
Note: Dashboard JSON under /api/* uses session cookies + CSRF and is separate
2026-08-17 21:20:45 +02:00
from this public API-key surface. Other path aliases
(/categories/create, /attributes/create, /campaigns) appear next to canonical paths.
servers:
- url: https://descrybe.io/api/v1
description: Production (Descrybe-hosted)
- url: http://localhost:28472/api/v1
description: Local web (Vite proxy to Go API)
- url: http://localhost:28471/api/v1
description: Local API (default Go listen address)
security:
- BearerAuth: []
- ApiKeyAuth: []
tags:
- name: Health
- name: Products
- name: Categories
- name: Attributes
- name: Feeds
- name: Export feeds
- name: Campaigns
2026-08-17 21:20:45 +02:00
description: Seasonal content calendar (/campaigns aliases)
- name: Processing
- name: Team
description: Dashboard session routes under /api (not API-key)
- name: Admin
description: Platform-admin dashboard routes under /api (not API-key)
paths:
/health:
get:
tags:
- Health
security: []
summary: Liveness (same payload as /healthz)
description: |
Returns process liveness plus cutover flags (maintenance, read_only, hypercare).
Host-level probes /healthz and /readyz share the same flag fields;
/readyz additionally reports database checks.
responses:
'200':
description: Liveness OK. No API key required. Same flag fields as host /healthz (status, service,
maintenance, read_only, hypercare).
content:
application/json:
schema:
$ref: "#/components/schemas/HealthStatus"
example:
status: ok
service: api
maintenance: false
read_only: false
hypercare: false
'500':
description: Unexpected failure building the health payload (rare process fault).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: health check failed
/openapi.yaml:
get:
tags:
- Health
security: []
summary: This OpenAPI document
responses:
'200':
description: This OpenAPI document. Served with Cache-Control and ETag; gzip when Accept-Encoding
allows.
content:
application/yaml:
schema:
type: string
example: |
openapi: 3.0.3
info:
title: Descrybe Public API
version: "1.0"
'304':
description: Not Modified — request If-None-Match matched the document ETag. Empty body.
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
/products:
get:
tags:
- Products
summary: List products
description: |
2026-08-17 21:20:45 +02:00
Public product list. Envelope is { data, meta } (not flat products/offset).
Query params: page, limit (default 25), status, search, sortBy, sortOrder, feedId.
Each row matches PresentProduct (id = processed_products.id, raw_product_id = raw_products.id,
product_id, name, category, status, feed_id, quality_score, quality_grade, created_at, updated_at).
For POST /products/process or POST /process dual-mode bodies, pass raw_product_id — not id.
parameters:
- $ref: "#/components/parameters/Page"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/parameters/PublicLimit"
- in: query
name: status
schema:
type: string
enum:
- all
- needs_review
- processed
- completed
- error
- processing
- unprocessed
default: all
description: |
Filter by product status. all (default) returns every status. needs_review also matches
2026-08-17 21:20:45 +02:00
status processed (pre-P0-8 AI review queue). Use completed after Accept enrichment.
- in: query
name: search
schema:
type: string
maxLength: 200
description: |
Free-text search over product name / product_id. Alias q is also accepted.
Omit or empty to skip text filtering. Max ~200 characters.
- in: query
name: sortBy
schema:
type: string
enum:
- updatedAt
- createdAt
- name
default: updatedAt
description: |
Sort column for the product list. One of updatedAt (default), createdAt, or name.
- in: query
name: sortOrder
schema:
type: string
enum:
- asc
- desc
default: desc
description: |
Sort direction. asc or desc (default desc). Combined with sortBy.
- in: query
name: feedId
schema:
type: string
format: uuid
description: |
Restrict results to products from this input feed. UUID format.
Alias feed_id is also accepted. Omit to include all feeds.
responses:
'200':
description: "Paged processed products for the API-key company. Returned after ListProcessedProductsDetailed\
\ succeeds. Envelope is data[] + meta (page, limit, total, totalPages)."
content:
application/json:
schema:
$ref: "#/components/schemas/ProductListResponse"
example:
data:
- id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
product_id: SKU-1001
name: Wireless earbuds
category: electronics/audio
status: completed
feed_id: 22222222-2222-2222-2222-222222222222
quality_score: 72
quality_grade: C
created_at: '2026-08-01T10:15:00Z'
updated_at: '2026-08-03T14:22:11Z'
meta:
page: 1
limit: 25
total: 1284
totalPages: 52
'400':
description: Client-facing catalog validation on filters (for example an invalid feed id). handleV1ListProducts
uses v1Err validation_error.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: validation_error
message: invalid feed id
'500':
description: Unexpected database/list failure in handleV1ListProducts.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: internal_error
message: list failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
/products/quality:
get:
tags:
- Products
summary: List product quality scores
description: |
2026-08-17 21:20:45 +02:00
Quality listing with { data, meta }. Defaults status=completed.
Optional min_score filters rows after scoring.
parameters:
- $ref: "#/components/parameters/Page"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/parameters/PublicLimit"
- in: query
name: status
schema:
type: string
enum:
- all
- needs_review
- processed
- completed
- error
- processing
- unprocessed
default: completed
description: |
Filter by product status. Default completed (quality scores are most useful after enrichment).
all returns every status. Same values as GET /products status.
- in: query
name: search
schema:
type: string
maxLength: 200
description: |
Free-text search over product name / product_id. Alias q also accepted. Omit to skip.
- in: query
name: min_score
schema:
type: integer
minimum: 0
maximum: 100
description: |
Minimum quality score (0-100 inclusive). Rows below this value are excluded after scoring.
Omit for no score floor.
- in: query
name: feedId
schema:
type: string
format: uuid
description: |
Restrict to products from this input feed UUID. Alias feed_id also accepted.
responses:
'200':
description: Quality rows for processed products (default status=completed). Returned after
list+score. min_score filters in-process after scoring. Envelope data + meta (page, limit,
total).
content:
application/json:
schema:
$ref: "#/components/schemas/ProductQualityListResponse"
example:
data:
- id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
product_id: SKU-1001
name: Wireless earbuds
quality_score: 72
quality_grade: C
quality_checks:
title: true
description: true
attributes: false
meta:
page: 1
limit: 25
total: 410
'400':
description: Query min_score is present but not an integer.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: validation_error
message: invalid min_score
'500':
description: Unexpected list failure in handleV1ListProductQuality.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: internal_error
message: list failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
/products/reset:
post:
tags:
- Products
summary: Reset products to unprocessed
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- product_ids
properties:
product_ids:
type: array
minItems: 1
items:
type: string
format: uuid
description: |
Processed (or raw, when kind=raw) product UUIDs to return to unprocessed.
Non-empty array of UUID strings.
kind:
type: string
enum:
- processed
- raw
default: processed
description: |
Which table the ids refer to. processed (default) resets enriched products;
raw targets raw_products rows instead.
example:
product_ids:
- a1b2c3d4-e5f6-7890-abcd-ef1234567890
- b2c3d4e5-f6a7-8901-bcde-f12345678901
kind: processed
responses:
'200':
description: Selected products returned to unprocessed. Returned when ResetProductsToUnprocessed
commits. Requires company-admin capability (API keys use role api and pass).
content:
application/json:
schema:
type: object
required:
- success
- reset_count
- message
properties:
success:
type: boolean
reset_count:
type: integer
message:
type: string
example:
success: true
reset_count: 12
message: 12 product(s) returned to unprocessed state
'400':
description: Invalid JSON, invalid product_ids UUID, empty product_ids, over max batch, or catalog.ClientError
from reset.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: product_ids is required
'403':
description: Caller role is neither admin nor api (requireCompanyAdmin). Valid company API keys
use role api and do not hit this.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'500':
description: Unexpected reset failure after validation.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: could not reset products
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/products/{id}:
parameters:
- $ref: "#/components/parameters/ID"
get:
tags:
- Products
summary: Get processed product
description: |
2026-08-17 21:20:45 +02:00
Public GET returns a data envelope without mapped_data, has_* flags, or processed_* internals.
Public fields match process items: name, description, attributes, images, eprel.
List endpoints use data plus meta.
responses:
'200':
2026-08-17 21:20:45 +02:00
description: "Single processed product as a data envelope (no mapped_data)."
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
type: object
required: [data]
properties:
data:
$ref: "#/components/schemas/PresentProduct"
example:
2026-08-17 21:20:45 +02:00
data:
id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
product_id: SKU-1001
name: Wireless earbuds
status: completed
'400':
description: Path id is not a UUID.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid id
'404':
description: Product not found for this company (or wrong tenant).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
patch:
tags:
- Products
summary: Update processed product
2026-08-17 21:20:45 +02:00
description: "Flat ProcessedProduct JSON (not a { data } envelope)."
requestBody:
content:
application/json:
schema:
type: object
description: Partial update — only send fields to change
properties:
processed_name:
type: string
2026-08-17 21:20:45 +02:00
description: Enriched display title
processed_description:
type: string
2026-08-17 21:56:09 +02:00
description: |
Enriched product body as one HTML string from the category
description formula. Length follows that formula (including very
large multi-section copy), not a short summary.
status:
type: string
enum:
- needs_review
- processed
- completed
- error
- processing
- unprocessed
description: Product workflow status. Use completed after accepting enrichment.
category:
type: string
description: Category path or unique_id string stored on the product
attributes:
type: object
additionalProperties: true
description: Source/raw attribute map (string keys to scalar or list values)
processed_attributes:
type: object
additionalProperties: true
description: Enriched attribute map after processing
example:
processed_name: Sony WH-1000XM5 Wireless Noise Cancelling Headphones Black
status: completed
attributes:
color: Black
brand: Sony
battery_life_hours: '30'
responses:
'200':
description: Product updated. Flat processed product JSON when UpdateProcessedProduct succeeds.
content:
application/json:
schema:
$ref: "#/components/schemas/ProcessedProduct"
example:
id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
product_id: SKU-1001
name: Wireless earbuds Pro
status: completed
'400':
description: Invalid JSON, invalid id, or catalog.ClientError / ClientOrLog on update.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: could not update product
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'404':
description: Resource id not found for this API-key company (or wrong tenant).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/products/process:
post:
tags:
- Processing
2026-08-17 21:20:45 +02:00
summary: Start processing by EAN
description: |
2026-08-17 21:56:09 +02:00
Public process API. Upserts raw products from items[].ean (GTIN), enqueues
one processing job, returns HTTP 200 with a data envelope.
Default processing_type full runs the full pipeline including AI enhance:
the model writes a retail title (name) and a full HTML product description
driven by the category description formula. That body can be long —
multiple headings, paragraphs, and lists, including very large
multi-section articles — not a one- or two-sentence summary.
Primary body: items[].ean. Alternate body on the same handler: raw_product_ids
(raw_products.id UUID list — not GET /products data[].id). Prefer items for public
integrations. On items[].ean, assertV1ProcessGates runs before EnsureRaw so plan/credit
2026-08-17 21:20:45 +02:00
failures cannot spam catalog writes (HTTP 402 coded envelope).
2026-08-17 21:56:09 +02:00
Poll GET /products/process/{id} until status is COMPLETED, then read
items[].description for the generated HTML.
Not an alias of POST /process (flat ProcessingJob / 202). Do not mix envelopes.
requestBody:
2026-08-17 21:20:45 +02:00
$ref: "#/components/requestBodies/StartProcessByEAN"
responses:
'200':
2026-08-17 21:20:45 +02:00
description: "Process job accepted. Always HTTP 200 (not 202) with data.process_id when\
\ enqueue succeeds. Prefer items[].ean; raw_product_ids alternate body is accepted on the\
\ same path."
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/ProcessStartEnvelope"
example:
data:
process_id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
status: PENDING
processing_type: full
'400':
description: Missing body, invalid processing_type, items without ean, invalid raw_product_id,
empty items, or other validation_error from handleV1StartProcess / v1ErrFromProcessing.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: validation_error
message: '''items'' array is required'
'402':
description: |
Plan gate blocked starting processing (billing credits/limits/AI/EPREL/feature flags).
2026-08-17 21:20:45 +02:00
v1ErrFromProcessing maps these to HTTP 402 with a coded envelope.
Codes: insufficient_credits, product_limit, ai_requires_upgrade, eprel_requires_upgrade,
plan_gate (message feature_disabled when the platform feature flag is off).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
examples:
insufficient_credits:
summary: Credits
value:
error:
code: insufficient_credits
message: Insufficient credits
feature_disabled:
summary: Feature flag
value:
error:
code: plan_gate
message: feature_disabled
"429": { $ref: "#/components/responses/TooManyRequests" }
'500':
2026-08-17 21:20:45 +02:00
description: Enqueue or unexpected internal failure starting the job.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: internal_server_error
message: Internal server error
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/products/process/{id}:
get:
tags:
- Processing
2026-08-17 21:20:45 +02:00
summary: Processing job status by process_id
description: |
2026-08-17 21:20:45 +02:00
Poll job status. Path param is process_id from POST /products/process.
Completed jobs return items[] (EAN-keyed enrichment). In-progress and failed
jobs omit items. Not the same shape as GET /process/{id} (flat ProcessingJob).
2026-08-17 09:33:07 +02:00
On COMPLETED items, name is the product display name (title is omitted when
2026-08-17 21:56:09 +02:00
identical). description is the generated product body: one HTML string
following the category description formula (h1/h2/h3/h4, p, ul). Expect
real catalog copy — often several paragraphs and lists, sometimes a very
large article — not a one- or two-sentence blurb. Internal UUIDs (id /
processed_product_id / raw_product_id) are omitted — use GET /products for
those. Optional meta_title and meta_description are short SEO fields and
may be omitted depending on company configuration.
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
description: |
process_id returned by POST /products/process. UUID format. Required.
responses:
'200':
2026-08-17 21:20:45 +02:00
description: "Job poll. Returned when the job exists for this company. status is uppercase;\
\ items[] appear when status is COMPLETED."
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/ProcessStatusEnvelope"
examples:
completed:
2026-08-17 21:20:45 +02:00
summary: Completed item
value:
data:
status: COMPLETED
process_id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
processing_type: full
items:
2026-08-16 16:57:36 +02:00
- ean: '8606019604493'
status: processed
2026-08-17 01:30:28 +02:00
category: Cookers
category_id: '50'
2026-08-16 16:57:36 +02:00
category_name: Cookers
name: VOX electric cooker EHT 6020 WG
2026-08-17 21:56:09 +02:00
description: "<h1>VOX electric cooker EHT 6020 WG</h1><p>The VOX EHT 6020 WG is a free-standing electric cooker for everyday household cooking. Four ceramic hobs and a 65 L fan oven let you boil, fry, and bake at the same time, while energy class A helps keep running costs down. The 60 cm width fits a standard kitchen niche. An enamel oven cavity and removable inner door glass are designed for cleaning after heavy use.</p><p>Run sauces or pasta on the hob while the oven handles a roast or tray bake. Heat is even across the ceramic zones, and residual-heat indicators reduce the chance of accidental contact after cooking. Fan mode suits multi-level baking; conventional heat remains available for dishes that prefer still air. A full-width grill finishes cheese, gratins, and toast.</p><h2>Key features</h2><ul><li>Four electric ceramic hobs with residual-heat indicators</li><li>65 L fan oven plus grill, energy class A</li><li>60 cm free-standing body (0.6 m wide, 0.85 m high, 0.6 m deep)</li><li>Enamel cavity and removable inner door glass</li><li>60-month manufacturer warranty</li></ul><p>Install in a standard 60 cm cooker slot with the dedicated electric supply shown on the rating plate. Leave rear clearance for ventilation and use a heat-resistant worktop next to the hob. This product is for household kitchens, not commercial catering loads.</p><p>Use cookware that matches each zone so energy is not wasted around the pan. Wipe the ceramic surface regularly and empty crumbs from the oven floor to preserve appearance and efficiency. For spare parts, quote model EHT 6020 WG and the EAN on the rating plate.</p>"
2026-08-16 16:57:36 +02:00
attributes:
brand: Vox
product_model: EHT6020WG
main_image: https://images.example.com/products/vox-eht6020wg.jpg
2026-08-17 21:56:09 +02:00
more_images:
- https://images.example.com/products/vox-eht6020wg-side.jpg
2026-08-16 16:57:36 +02:00
eprel:
id: "1234567"
label: https://eprel.ec.europa.eu/label/Example
pdf: https://eprel.ec.europa.eu/fiches/Example.pdf
energy_class: A
energy_scale: A-G
total_items: 1
processed_at: '2026-08-04T10:04:12Z'
processing:
summary: In progress
value:
data:
status: PROCESSING
process_id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
processing_type: full
'400':
description: Path id is not a UUID.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: validation_error
message: invalid id
'404':
description: No processing job with this id for the API-key company.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: not_found
message: Processing job not found
'500':
description: Unexpected failure loading job status.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: internal_server_error
message: Internal server error
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/categories:
get:
tags:
- Categories
summary: List categories
2026-08-17 21:20:45 +02:00
description: "Paged list — { data, meta } with page/limit pagination."
parameters:
- name: page
in: query
schema:
type: integer
minimum: 1
default: 1
description: |
1-based page index. Integer, default 1, minimum 1.
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 25
description: |
Page size. Integer, default 25, minimum 1, maximum 100.
- in: query
name: search
schema:
type: string
maxLength: 200
description: |
Free-text search over category name / unique_id. Alias of q; either may be sent.
- in: query
name: q
schema:
type: string
maxLength: 200
description: |
Canonical search query (same as search). Omit both to return the full page.
responses:
'200':
description: "Paged categories as data[] + meta. Returned after ListCategories succeeds."
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CategoriesEnvelope"
example:
data:
- id: 33333333-3333-3333-3333-333333333333
unique_id: electronics
name: Electronics
created_at: '2026-07-01T08:00:00Z'
updated_at: '2026-07-15T12:00:00Z'
meta:
page: 1
limit: 25
total: 42
totalPages: 2
'500':
description: Unexpected list failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: list failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
post:
tags:
- Categories
summary: Create category
description: Requires name and unique_id. parent_id is accepted as an alias of parent_unique_id.
requestBody:
$ref: "#/components/requestBodies/CreateCategory"
responses:
'201':
description: Category created. HTTP 201 with data containing id, unique_id, name.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CategoryCreateEnvelope"
example:
data:
id: 33333333-3333-3333-3333-333333333333
unique_id: electronics
name: Electronics
'400':
description: Invalid JSON, missing name/unique_id, duplicate unique_id, bad parent, or other
catalog.ClientError from CreateCategory.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: could not create category
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/categories/create:
post:
tags:
- Categories
2026-08-17 21:20:45 +02:00
summary: Create category (alias of POST /categories)
description: "Alias of POST /categories. Same body and { data } response."
requestBody:
$ref: "#/components/requestBodies/CreateCategory"
responses:
'201':
description: Category created. HTTP 201 with data containing id, unique_id, name.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CategoryCreateEnvelope"
example:
data:
id: 33333333-3333-3333-3333-333333333333
unique_id: electronics
name: Electronics
'400':
description: Invalid JSON, missing name/unique_id, duplicate unique_id, bad parent, or other
catalog.ClientError from CreateCategory.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: could not create category
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/categories/{id}:
parameters:
- name: id
in: path
required: true
schema:
type: string
description: |
For GET/PATCH — category UUID. For DELETE — category unique_id slug (e.g. electronics).
Required. Format depends on the method.
get:
tags:
- Categories
summary: Get category by UUID
2026-08-17 21:20:45 +02:00
description: |
Public GET returns a data envelope of EnvelopeCategory (no prompts or formulas). Path id must be the category UUID.
List endpoints use data plus meta; this GET omits meta.
responses:
'200':
2026-08-17 21:20:45 +02:00
description: Category detail as a data envelope of EnvelopeCategory when found.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
type: object
required: [data]
properties:
data:
$ref: "#/components/schemas/EnvelopeCategory"
example:
2026-08-17 21:20:45 +02:00
data:
id: 33333333-3333-3333-3333-333333333333
unique_id: electronics
name: Electronics
'400':
description: Invalid category id.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid id
'404':
description: Category not found for this company.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected get failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: get failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
patch:
tags:
- Categories
summary: Update category by UUID
2026-08-17 21:20:45 +02:00
description: "Flat updated category JSON (not a { data } envelope)."
requestBody:
content:
application/json:
schema:
type: object
description: Partial update — only send fields to change
properties:
name:
type: string
description: Display name shown in the catalog UI
unique_id:
type: string
description: |
Stable slug identifier (lowercase letters, digits, underscores/hyphens).
Changing it may break feed mappings that reference the old id.
parent_unique_id:
type: string
nullable: true
description: Parent category unique_id, or null for a root category
description:
type: string
nullable: true
description: Optional human-readable category description
is_active:
type: boolean
description: When false, category is hidden from active catalog selection
position:
type: integer
description: Sort order among siblings (lower first). Non-negative integer.
example:
name: Consumer Electronics
description: Updated root for consumer devices
is_active: true
position: 0
responses:
'200':
description: Category updated (flat object) when PATCH succeeds.
content:
application/json:
schema:
$ref: "#/components/schemas/CategoryDetail"
example:
id: 33333333-3333-3333-3333-333333333333
unique_id: electronics
name: Consumer Electronics
'400':
description: Invalid JSON or catalog client error.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid json
'404':
description: Category not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
delete:
tags:
- Categories
summary: Delete category by unique_id
2026-08-17 21:20:45 +02:00
description: Path id is the category unique_id.
responses:
'200':
description: Category deleted by unique_id path param. Returned when DeleteCategoryByUniqueID
succeeds. Envelope data.message.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/SuccessMessage"
example:
data:
message: Category deleted successfully
'400':
description: Empty unique_id or catalog.ClientError blocking delete.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid category id
'404':
description: No category with this unique_id for the company.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected delete failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: delete failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/attributes:
get:
tags:
- Attributes
summary: List attributes
2026-08-17 21:20:45 +02:00
description: "Paged list — { data, meta }."
parameters:
- name: page
in: query
schema:
type: integer
minimum: 1
default: 1
description: |
1-based page index. Integer, default 1, minimum 1.
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 25
description: |
Page size. Integer, default 25, minimum 1, maximum 100.
- in: query
name: search
schema:
type: string
maxLength: 200
description: |
Free-text search over attribute name / key. Alias of q.
- in: query
name: q
schema:
type: string
maxLength: 200
description: |
Canonical search query (same as search). Omit both for an unfiltered page.
- in: query
name: categoryId
schema:
type: string
description: |
Filter by category unique_id slug (e.g. electronics), not UUID.
- in: query
name: sortBy
schema:
type: string
enum:
- name
- attributeKey
- updatedAt
default: updatedAt
description: |
Sort column. name, attributeKey, or updatedAt (default).
- in: query
name: sortOrder
schema:
type: string
enum:
- asc
- desc
default: desc
description: |
Sort direction. asc or desc (default desc).
responses:
'200':
description: "Paged attributes as data[] + meta (presentV1Attribute)."
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/AttributesEnvelope"
example:
data:
- id: 44444444-4444-4444-4444-444444444444
key: color
name: Color
type: text
unit: ""
required: false
category_unique_id: electronics
created_at: '2026-07-01T08:00:00Z'
updated_at: '2026-07-15T12:00:00Z'
meta:
page: 1
limit: 25
total: 18
totalPages: 1
'500':
description: Unexpected list failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: list failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
post:
tags:
- Attributes
summary: Create attribute
description: Requires name, attribute_key, value_type, and category_unique_id; links the attribute
to that category.
requestBody:
$ref: "#/components/requestBodies/CreateAttribute"
responses:
'201':
description: Attribute created (and linked when category_unique_id resolves). HTTP 201 data
envelope.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/AttributeCreateEnvelope"
example:
data:
id: 44444444-4444-4444-4444-444444444444
key: color
name: Color
type: text
category_unique_id: electronics
'400':
description: Invalid JSON, missing required fields, or catalog.ClientError on create/link.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: "Missing required fields: name, attribute_key, value_type, category_unique_id"
'404':
description: Link target category was not found (when link step maps to not found).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected create failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: could not create attribute
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/attributes/create:
post:
tags:
- Attributes
2026-08-17 21:20:45 +02:00
summary: Create attribute (alias of POST /attributes)
description: Alias of POST /attributes.
requestBody:
$ref: "#/components/requestBodies/CreateAttribute"
responses:
'201':
description: Attribute created (and linked when category_unique_id resolves). HTTP 201 data
envelope.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/AttributeCreateEnvelope"
example:
data:
id: 44444444-4444-4444-4444-444444444444
key: color
name: Color
type: text
category_unique_id: electronics
'400':
description: Invalid JSON, missing required fields, or catalog.ClientError on create/link.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: "Missing required fields: name, attribute_key, value_type, category_unique_id"
'404':
description: Link target category was not found (when link step maps to not found).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected create failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: could not create attribute
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/attributes/{id}:
parameters:
- $ref: "#/components/parameters/ID"
patch:
tags:
- Attributes
summary: Update attribute
2026-08-17 21:20:45 +02:00
description: Flat attribute JSON (attribute_key / value_type fields — not key/type aliases).
requestBody:
content:
application/json:
schema:
type: object
description: Partial update — only send fields to change
properties:
name:
type: string
description: Human-readable attribute label shown in the UI
value_type:
type: string
enum: [string, number, list, multiselect]
description: |
Value shape for this attribute. string, number, list, or multiselect.
unit:
type: string
nullable: true
description: Optional unit label (e.g. W, cm). Null clears the unit.
example:
type: string
nullable: true
description: Sample value for docs/UI hints (e.g. Black)
example:
name: Color
value_type: string
example: Black
responses:
'200':
description: Attribute updated when PATCH succeeds (flat AttributeDetail / shared handler).
content:
application/json:
schema:
$ref: "#/components/schemas/AttributeDetail"
example:
id: 44444444-4444-4444-4444-444444444444
attribute_key: color
name: Colour
'400':
description: Invalid JSON or catalog client error.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid json
'404':
description: Attribute not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
delete:
tags:
- Attributes
summary: Delete attribute by UUID
responses:
'200':
description: Attribute deleted by UUID. Returned when DeleteAttribute succeeds. data.message
envelope.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/SuccessMessage"
example:
data:
message: Attribute deleted successfully
'400':
description: Path id is not a UUID.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid attribute id
'404':
description: Attribute not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected delete failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: delete failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/feeds:
get:
tags:
- Feeds
summary: List input feeds
description: |
2026-08-17 21:20:45 +02:00
Paged list — { data: Feed[], meta: { page, limit, total } }.
Accepts page+limit (defaults page=1, limit=25, max 100) or limit+offset.
Each feed includes presentFeed fields plus dual-support v2 keys (feed_type, sync_interval_minutes, options).
parameters:
- name: page
in: query
schema:
type: integer
minimum: 1
default: 1
description: |
2026-08-17 21:20:45 +02:00
1-based page index for page+limit mode. Integer, default 1, minimum 1.
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 25
description: |
Page size. Integer, default 25, minimum 1, maximum 100. Also used with offset.
- $ref: "#/components/parameters/Offset"
- in: query
name: q
schema:
type: string
maxLength: 200
description: |
Free-text search over feed name / URL. Canonical search param.
- in: query
name: search
schema:
type: string
maxLength: 200
description: |
Alias of q. Same free-text search over feed name / URL.
responses:
'200':
description: Paged feeds with meta (including active_total, mapped_total). Returned after Feeds.List.
content:
application/json:
schema:
$ref: "#/components/schemas/FeedListResponse"
example:
data:
- id: 22222222-2222-2222-2222-222222222222
name: Main XML feed
url: https://supplier.example/feed.xml
item_path: products/product
is_active: true
meta:
page: 1
limit: 25
total: 3
totalPages: 1
offset: 0
active_total: 2
mapped_total: 1
'500':
description: Unexpected list failure (v1Err).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: internal_error
message: list failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
post:
tags:
- Feeds
summary: Create feed
description: |
2026-08-17 21:20:45 +02:00
Body requires name + item_path (url optional). Dual-support also accepts
feed_type, sync_interval_minutes, sync_frequency (hours), and multipart file uploads.
requestBody:
content:
application/json:
schema:
type: object
required: [name, item_path]
properties:
name:
type: string
description: Display name for this input feed (required)
item_path:
type: string
description: |
XML item xpath / path stored in options.item_path (e.g. channel/item). Required.
url:
type: string
format: uri
nullable: true
description: |
HTTPS (or HTTP) URL of the remote feed. Nullable for upload-only feeds.
feed_type:
type: string
enum:
- xml
- csv
description: Source format. xml (default) or csv.
sync_interval_minutes:
type: integer
minimum: 1
default: 60
description: |
How often automatic sync should run, in minutes. Default 60. Minimum 1.
sync_frequency:
type: integer
minimum: 1
description: |
2026-08-17 21:20:45 +02:00
Interval in hours. Converted to minutes when sync_interval_minutes is unset.
is_active:
type: boolean
description: |
When true, status is set to active after create; when false/omitted, typically unmapped.
example:
name: Nordic Webshop Google Merchant XML
item_path: channel/item
url: https://feeds.example.com/nordic/google-merchant.xml
feed_type: xml
sync_interval_minutes: 60
is_active: true
responses:
'201':
description: Feed created from JSON or multipart. HTTP 201 data envelope when Create succeeds.
is_active is honored for UUID ids.
content:
application/json:
schema:
$ref: "#/components/schemas/FeedCreateResponse"
example:
data:
id: 22222222-2222-2222-2222-222222222222
name: Main XML feed
url: https://supplier.example/feed.xml
item_path: products/product
is_active: true
'400':
description: Invalid JSON/multipart, missing name/item_path (or name+url), upload failure, or
feeds.ClientError.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: validation_error
message: "Missing required fields: name, item_path"
'500':
description: Unexpected create failure.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: internal_error
message: could not create feed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/feeds/{id}:
parameters:
- $ref: "#/components/parameters/ID"
get:
tags:
- Feeds
summary: Get feed
2026-08-17 21:20:45 +02:00
description: "Envelope — { data: Feed }."
responses:
'200':
description: Single feed in data envelope when Feeds.Get succeeds for this company.
content:
application/json:
schema:
$ref: "#/components/schemas/FeedGetResponse"
example:
data:
id: 22222222-2222-2222-2222-222222222222
name: Main XML feed
url: https://supplier.example/feed.xml
item_path: products/product
is_active: true
'400':
description: Path id is not a UUID.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: validation_error
message: Invalid id
'404':
description: Feed not found for this company.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: not_found
message: Not found
'500':
description: Unexpected get failure.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: internal_error
message: get failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
patch:
tags:
- Feeds
summary: Update feed
description: "Returns flat PresentFeed JSON (same shape as data in GET /feeds/{id}, without the\
\ data wrapper)."
requestBody:
content:
application/json:
schema:
type: object
description: Partial update — only send fields to change
properties:
name:
type: string
description: Display name for this input feed
url:
type: string
format: uri
nullable: true
description: Remote feed URL, or null to clear
item_path:
type: string
description: XML item xpath / path (stored in options.item_path)
feed_type:
type: string
enum:
- xml
- csv
description: Source format. xml or csv.
status:
type: string
description: |
Feed lifecycle status (e.g. active, unmapped, error). Handler validates allowed values.
sync_interval_minutes:
type: integer
minimum: 1
description: Automatic sync interval in minutes. Minimum 1.
sync_frequency:
type: integer
minimum: 1
description: |
2026-08-17 21:20:45 +02:00
Interval in hours. Converted to minutes when sync_interval_minutes is unset.
example:
name: Nordic Webshop Google Merchant XML (EU)
sync_interval_minutes: 120
status: active
responses:
'200':
description: Feed updated. Flat PresentFeed from shared handleUpdateFeed.
content:
application/json:
schema:
$ref: "#/components/schemas/PresentFeed"
example:
id: 22222222-2222-2222-2222-222222222222
name: Main XML feed
is_active: false
'400':
description: Invalid id/JSON or feeds.ClientError.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid json
'404':
description: Feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
delete:
tags:
- Feeds
summary: Delete feed
2026-08-17 21:20:45 +02:00
description: "Company-admin only. Flat { id, deleted: true } (not a message envelope)."
responses:
'200':
description: Feed deleted. Flat FeedDeleted when handleDeleteFeed succeeds. Requires company-admin
capability (API keys pass as role api).
content:
application/json:
schema:
$ref: "#/components/schemas/FeedDeleted"
example:
id: 22222222-2222-2222-2222-222222222222
deleted: true
'400':
description: Invalid feed UUID.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid id
'403':
description: requireCompanyAdmin rejected the caller (session non-admin). API keys pass.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'404':
description: Feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected delete failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: delete failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/feeds/{id}/sync:
post:
tags:
- Feeds
summary: Trigger feed sync
description: |
2026-08-17 21:20:45 +02:00
Contract — HTTP 200 { data: { jobId } } (not 202). Enqueues a durable
feed_sync_jobs row and wakes the worker via NOTIFY; the API process does not
run sync work. Dual-support also returns job_id. Dashboard POST /api/feeds/{id}/sync
uses HTTP 202 with a flat job object — do not mix envelopes.
parameters:
- $ref: "#/components/parameters/ID"
responses:
"200":
description: Sync job durably enqueued. Returned when EnqueueSync succeeds. data.jobId (+ job_id alias).
content:
application/json:
schema:
$ref: "#/components/schemas/FeedSyncResponse"
example:
data:
jobId: 55555555-5555-5555-5555-555555555555
job_id: 55555555-5555-5555-5555-555555555555
'400':
description: Invalid id or feeds.ClientError (inactive feed, bad source, …).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: validation_error
message: Invalid id
'404':
description: Feed not found.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: not_found
message: Feed not found
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid id
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: Invalid id
'500':
description: Failed to create sync job.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: internal_error
message: Failed to create sync job
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'429':
description: Rate limit exceeded for heavy mutations (30/min/company). Retry-After 60.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: rate limit exceeded
/feeds/{id}/mappings:
parameters:
- $ref: "#/components/parameters/ID"
get:
tags:
- Feeds
summary: Get feed mappings
description: "Active feed_mappings row, or { mappings: [] } when none exist."
responses:
'200':
description: Active feed_mappings document for the feed, or an empty mappings payload when none
exist.
content:
application/json:
schema:
$ref: "#/components/schemas/FeedMappings"
example:
mappings:
title: name
ean: gtin
'400':
description: Invalid feed UUID.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid id
'404':
description: Feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected mappings read failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: list failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
put:
tags:
- Feeds
summary: Put feed mappings
description: Replace active mappings (bumps version).
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- mappings
properties:
mappings:
description: Mapping document (object or array)
example:
mappings:
item_path: channel/item
fields:
- source: g:id
target: product_id
- source: title
target: name
- source: g:gtin
target: gtin
- source: g:brand
target: brand
- source: g:image_link
target: main_image
responses:
'200':
description: Mappings replaced when PUT body validates and save succeeds.
content:
application/json:
schema:
$ref: "#/components/schemas/FeedMappings"
example:
mappings:
title: name
ean: gtin
'400':
description: Invalid id/JSON or mapping validation error.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid json
'404':
description: Feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/feeds/{id}/extract-schema:
post:
tags:
- Feeds
summary: Extract feed schema sample paths and preview
description: Samples the feed source and returns discovered field paths plus a short preview.
parameters:
- $ref: "#/components/parameters/ID"
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
item_path:
type: string
description: Optional XML item path hint; inferred when omitted
example:
item_path: channel/item
responses:
'200':
description: Sample schema/fields extracted from the feed source.
content:
application/json:
schema:
$ref: "#/components/schemas/SchemaExtractResult"
example:
fields:
- name
- gtin
- price
item_path: products/product
'400':
description: Invalid id or extract client error.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid id
'404':
description: Feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected extract failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: extract failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
"429": { $ref: "#/components/responses/TooManyRequests" }
/feeds/{id}/sync-process-sample:
post:
tags:
- Feeds
summary: Sync feed then process a sample of raw products
description: |
Optionally syncs the feed, then starts dashboard-style processing for up to N raw products
(default 10, max 100). Flat JSON; HTTP 202 when a job is queued, 200 when sync finishes with no products.
parameters:
- $ref: "#/components/parameters/ID"
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
limit:
type: integer
minimum: 1
maximum: 100
default: 10
description: |
Max raw products to queue after sync. Integer, default 10, minimum 1, maximum 100.
skip_sync:
type: boolean
default: false
description: |
When true, skip the feed sync step and process existing raw products only.
processing_type:
type: string
default: full
description: |
Pipeline mode — full (default) or a single step name (category, title, description, attributes).
example:
limit: 10
skip_sync: false
processing_type: full
responses:
'200':
description: Sample sync finished but produced no raw products to process (sync completed empty).
content:
application/json:
schema:
type: object
example:
ok: true
imported: 0
message: no products
'202':
description: Sample sync imported products and queued processing (async accept).
content:
application/json:
schema:
type: object
example:
accepted: true
job_id: 55555555-5555-5555-5555-555555555555
imported: 5
'400':
description: Invalid id or sample validation error.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid id
'404':
description: Feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected sample failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: sample failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
"429": { $ref: "#/components/responses/TooManyRequests" }
/export-feeds:
get:
tags:
- Export feeds
summary: List export feeds
description: |
2026-08-17 21:20:45 +02:00
Envelope — { data: ExportFeed[], meta: { page, limit, total } }.
Accepts page+limit (defaults page=1, limit=25, max 100) or limit+offset.
parameters:
- name: page
in: query
schema:
type: integer
default: 1
minimum: 1
description: |
2026-08-17 21:20:45 +02:00
1-based page index for page+limit mode. Integer, default 1, minimum 1.
- $ref: "#/components/parameters/PublicLimit"
- $ref: "#/components/parameters/Offset"
responses:
'200':
description: "Paged export feeds as data[] + meta (presentV1ExportFeed rows)."
content:
application/json:
schema:
type: object
required:
- data
- meta
properties:
data:
type: array
items:
$ref: "#/components/schemas/ExportFeedDetail"
meta:
type: object
example:
data:
- id: 66666666-6666-6666-6666-666666666666
name: Google Shopping
format: xml
is_active: true
meta:
page: 1
limit: 25
total: 1
'500':
description: Unexpected list failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: list failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
post:
tags:
- Export feeds
summary: Create export feed
2026-08-17 21:20:45 +02:00
description: "Envelope — { data: ExportFeed }."
requestBody:
content:
application/json:
schema:
type: object
required:
- name
- format
properties:
name:
type: string
description: Display name for the export feed (required)
format:
type: string
enum:
- xml
- csv
description: Output format. xml or csv (required).
source_feed_id:
type: string
format: uuid
description: Optional input feed UUID this export is derived from
template:
type: object
description: |
Export template document (root/item/mappings). Structure depends on format.
structure:
type: object
2026-08-17 21:20:45 +02:00
description: Alias for template — same object shape
mappings:
type: object
description: Field mapping object (source → target). May also live under template.
filters:
type: object
description: |
Product filters applied at generate time (e.g. statuses list). Object map.
root_xpath:
type: string
description: Optional XML root xpath hint for template builders
item_xpath:
type: string
description: Optional XML item xpath hint for template builders
example:
name: Warehouse Inventory CSV
format: csv
source_feed_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
filters:
statuses:
- completed
mappings:
title: processed_name
gtin: gtin
responses:
'201':
description: Export feed created when name+format validate. HTTP 201 data envelope.
content:
application/json:
schema:
type: object
required:
- data
properties:
data:
$ref: "#/components/schemas/ExportFeedDetail"
example:
data:
id: 66666666-6666-6666-6666-666666666666
name: Google Shopping
format: xml
'400':
description: "Invalid JSON, missing name/format, or feeds.ClientError. Note: ClientOrLog fallback\
\ also uses flat Error on some paths."
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: validation_error
message: "Missing required fields: name, format"
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/export-feeds/{id}:
parameters:
- $ref: "#/components/parameters/ID"
get:
tags:
- Export feeds
summary: Get export feed
description: Flat export feed row including template/filters (not the list presentV1ExportFeed /
data envelope).
responses:
'200':
description: Export feed detail when found.
content:
application/json:
schema:
$ref: "#/components/schemas/ExportFeedDetail"
example:
id: 66666666-6666-6666-6666-666666666666
name: Google Shopping
format: xml
'400':
description: Invalid UUID.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid id
'404':
description: Export feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected get failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: get failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
patch:
tags:
- Export feeds
summary: Update export feed
description: Flat updated export feed row.
requestBody:
content:
application/json:
schema:
type: object
description: Partial update — only send fields to change
properties:
name:
type: string
description: Display name for the export feed
is_active:
type: boolean
description: When false, public token URLs may still exist but feed is inactive
template:
type: object
description: Export template document (root/item/mappings)
filters:
type: object
description: Product filters applied at generate time
example:
name: Google Shopping XML EU
is_active: true
filters:
statuses:
- completed
responses:
'200':
description: Export feed updated.
content:
application/json:
schema:
$ref: "#/components/schemas/ExportFeedDetail"
example:
id: 66666666-6666-6666-6666-666666666666
name: Google Shopping EU
format: xml
'400':
description: Invalid JSON or client error.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid json
'404':
description: Export feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
delete:
tags:
- Export feeds
summary: Delete export feed
2026-08-17 21:20:45 +02:00
description: "Company-admin only. Flat { status: ok } (not SuccessMessage)."
responses:
'200':
description: Export feed deleted (admin capability; API keys pass). Flat deleted marker.
content:
application/json:
schema:
$ref: "#/components/schemas/FeedDeleted"
example:
id: 66666666-6666-6666-6666-666666666666
deleted: true
'400':
description: Invalid UUID.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid id
'403':
description: requireCompanyAdmin rejected session non-admin.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'404':
description: Export feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected delete failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: delete failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/export-feeds/{id}/template:
put:
tags:
- Export feeds
summary: Update export feed template
description: Updates template and/or filters; returns flat ExportFeedDetail.
parameters:
- $ref: "#/components/parameters/ID"
requestBody:
content:
application/json:
schema:
type: object
properties:
template:
type: object
description: |
Full export template document (root, item path, field mappings). Replaces prior template when set.
filters:
type: object
description: |
Product filters for generation (e.g. statuses). Replaces prior filters when set.
example:
template:
root: rss
item: channel/item
mappings:
title: processed_name
gtin: gtin
filters:
statuses:
- completed
responses:
'200':
description: Template saved for the export feed.
content:
application/json:
schema:
$ref: "#/components/schemas/ExportFeedDetail"
example:
id: 66666666-6666-6666-6666-666666666666
name: Google Shopping
format: xml
'400':
description: Invalid id/body or template validation.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid json
'404':
description: Export feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/export-feeds/{id}/rotate-token:
post:
tags:
- Export feeds
summary: Rotate public export URL token
description: |
Replaces public_token so the previous /api/public/export-feeds/{token}.{xml|csv}
URL stops working immediately. Requires company admin (or platform staff).
Response is a flat ExportFeedDetail including the new public_token.
parameters:
- $ref: "#/components/parameters/ID"
responses:
'200':
description: Token rotated; body includes the new public_token and URLs.
content:
application/json:
schema:
$ref: "#/components/schemas/ExportFeedDetail"
'404':
description: Export feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'401':
description: Missing or invalid API key.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
'403':
description: Admin required.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
"400": { $ref: "#/components/responses/BadRequest" }
"422": { $ref: "#/components/responses/ValidationError" }
"500": { $ref: "#/components/responses/InternalServerError" }
"429": { $ref: "#/components/responses/TooManyRequests" }
/export-feeds/{id}/generate:
post:
tags:
- Export feeds
summary: Generate export feed file
description: |
2026-08-17 21:20:45 +02:00
Envelope — { data: { generated, format, filePath, downloadUrl, ... } }.
Content is streamed live via public download URL (not persisted to disk).
parameters:
- $ref: "#/components/parameters/ID"
responses:
'200':
description: Generation finished. data includes generated, format, downloadUrl, products_exported,
status. filePath may be null — documented under examples.value for RapiDoc safety.
content:
application/json:
schema:
type: object
required:
- data
properties:
data:
type: object
examples:
ok:
summary: Generated
value:
data:
generated: true
format: xml
filePath: null
downloadUrl: /api/public/export-feeds/tok_abc.xml
products_exported: 1284
last_generated_at: '2026-08-04T12:00:00Z'
status: ready
'400':
description: Invalid id or generation_failed client error (empty template, …).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: generation_failed
message: template is empty
'404':
description: Export feed not found.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: not_found
message: Export feed not found
'500':
description: Generation failed internally.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: generation_failed
message: Failed to generate export feed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
"429": { $ref: "#/components/responses/TooManyRequests" }
/export-feeds/{id}/export-products:
post:
tags:
- Export feeds
summary: Export selected processed products
description: |
Streams the rendered export for the given processed product UUIDs.
Response body is CSV or XML bytes (not JSON). Headers include Content-Disposition and X-Products-Exported.
parameters:
- $ref: "#/components/parameters/ID"
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- product_ids
properties:
product_ids:
type: array
minItems: 1
items:
type: string
format: uuid
description: |
Processed product UUIDs to include in the streamed export. Non-empty array.
example:
product_ids:
- 2c5ea4c0-4067-4e44-8c5a-9a8b7c6d5e4f
- 550e8400-e29b-41d4-a716-446655440001
responses:
'200':
description: Selected products exported / file bytes produced for this export feed.
content:
application/json:
schema:
type: object
example:
exported: 25
skipped: 2
'400':
description: Invalid id/body or selection validation.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid json
'404':
description: Export feed not found.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'500':
description: Unexpected export failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: export failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
"429": { $ref: "#/components/responses/TooManyRequests" }
/campaigns:
get:
tags:
- Campaigns
2026-08-17 21:20:45 +02:00
summary: List seasonal campaign presets (alias of GET /marketing/calendar)
description: |
2026-08-17 21:20:45 +02:00
Alias of GET /marketing/calendar. Returns the same envelope
{ data: { year, presets, prepared } }. Not email campaigns.
parameters:
- name: year
in: query
schema:
type: integer
example: 2026
minimum: 2000
maximum: 2100
description: |
Calendar year for seasonal presets (e.g. 2026). Integer. When omitted, server uses the current year.
responses:
'200':
2026-08-17 21:20:45 +02:00
description: Alias of marketing calendar. data envelope around calendar payload (handleV1ListCampaigns
→ v1OK).
content:
application/json:
schema:
$ref: "#/components/schemas/MarketingCalendar"
example:
data:
year: 2026
presets:
- id: back_to_school
'400':
description: Calendar query validation failed (v1MarketingCalendar → v1Err).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: validation_error
message: invalid year
'500':
description: Unexpected calendar failure.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: internal_error
message: list failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
/campaigns/prepare:
post:
tags:
- Campaigns
2026-08-17 21:20:45 +02:00
summary: Prepare seasonal campaign export (alias of POST /marketing/calendar/prepare)
description: |
2026-08-17 21:20:45 +02:00
Alias of POST /marketing/calendar/prepare. Envelope
{ data: { preset_id, name, export_feed_id, created, ... } }.
requestBody:
content:
application/json:
schema:
type: object
required:
- preset_id
properties:
preset_id:
type: string
enum:
- black_friday
- christmas
description: |
Seasonal preset id. black_friday or christmas (required).
year:
type: integer
minimum: 2000
maximum: 2100
description: |
Target calendar year for date windows. Integer. Defaults to current year when omitted.
format:
type: string
enum:
- csv
- xml
default: csv
description: Export feed format for the prepared campaign. csv (default) or xml.
force_new:
type: boolean
description: |
When true, create a new export feed even if one already exists for this preset/year.
example:
preset_id: black_friday
year: 2026
format: csv
force_new: false
responses:
'200':
description: Prepare reused existing campaign. data envelope (PreparedCampaignEnvelope).
content:
application/json:
schema:
$ref: "#/components/schemas/PreparedCampaignEnvelope"
example:
data:
preset_id: back_to_school
year: 2026
created: false
'201':
description: Prepare created a new campaign. data envelope.
content:
application/json:
schema:
$ref: "#/components/schemas/PreparedCampaignEnvelope"
example:
data:
preset_id: back_to_school
year: 2026
created: true
'400':
description: Invalid JSON or marketing.ClientError (v1Err). ClientOrLog may emit flat Error.
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: validation_error
message: invalid json
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/marketing/calendar:
get:
tags:
- Campaigns
summary: List seasonal campaign presets
2026-08-17 21:20:45 +02:00
description: Canonical path (flat JSON). /campaigns is an alias of this path.
parameters:
- name: year
in: query
schema:
type: integer
example: 2026
minimum: 2000
maximum: 2100
description: |
Calendar year for seasonal presets (e.g. 2026). Integer. When omitted, server uses the current year.
responses:
'200':
description: Flat marketing calendar payload (year, presets, prepared) when query validates.
content:
application/json:
schema:
$ref: "#/components/schemas/MarketingCalendar"
example:
year: 2026
presets:
- id: back_to_school
label: Back to school
'400':
description: Invalid query (year/preset).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid date
'500':
description: Unexpected calendar failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: list failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
/marketing/calendar/prepare:
post:
tags:
- Campaigns
summary: Prepare seasonal campaign export
2026-08-17 21:20:45 +02:00
description: Canonical path (flat JSON). /campaigns/prepare is an alias of this path.
requestBody:
content:
application/json:
schema:
type: object
required:
- preset_id
properties:
preset_id:
type: string
enum:
- black_friday
- christmas
description: |
Seasonal preset id. black_friday or christmas (required).
year:
type: integer
minimum: 2000
maximum: 2100
description: |
Target calendar year for date windows. Integer. Defaults to current year when omitted.
format:
type: string
enum:
- csv
- xml
description: Export feed format for the prepared campaign. csv or xml.
force_new:
type: boolean
description: |
When true, create a new export feed even if one already exists for this preset/year.
example:
preset_id: black_friday
year: 2026
format: csv
force_new: false
responses:
'200':
description: Prepare reused an existing campaign (Created=false). Flat PreparedCampaign JSON.
content:
application/json:
schema:
$ref: "#/components/schemas/PreparedCampaign"
example:
preset_id: back_to_school
year: 2026
created: false
'201':
description: Prepare created a new campaign (Created=true). Flat PreparedCampaign JSON.
content:
application/json:
schema:
$ref: "#/components/schemas/PreparedCampaign"
example:
preset_id: back_to_school
year: 2026
created: true
'400':
description: Invalid JSON or marketing.ClientError.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid json
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/process:
get:
tags:
- Processing
summary: List processing jobs (raw_product_ids surface)
description: |
Flat job list for the /process + raw_product_ids contract.
2026-08-17 21:20:45 +02:00
Separate from GET /products/process/{id}.
parameters:
- $ref: "#/components/parameters/Limit"
responses:
'200':
description: "Recent dashboard-style jobs. Flat object with jobs[] and limit (handleV1ListProcessJobs\
2026-08-17 21:20:45 +02:00
\ — not a data envelope)."
content:
application/json:
schema:
type: object
required:
- jobs
- limit
properties:
jobs:
type: array
items:
$ref: "#/components/schemas/ProcessingJob"
limit:
type: integer
example:
jobs:
- id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
status: processing
total_products: 25
processed_products: 8
limit: 50
'500':
description: Unexpected job list failure.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: list failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
post:
tags:
- Processing
summary: Start processing job by raw_product_ids
description: |
Enqueues AI processing for existing raw product UUIDs.
Same pipeline as dashboard POST /api/processing/jobs.
Flat JSON body/response (no data wrapper); HTTP 202.
Pass raw_products.id only (GET /products data[].raw_product_id), never PresentProduct.id.
2026-08-17 21:20:45 +02:00
Separate from POST /products/process (items[].ean → 200 { data }).
Plan gates return HTTP 402 PlanGateError (not the coded envelope).
requestBody:
$ref: "#/components/requestBodies/StartProcessByRawIDs"
responses:
'202':
description: "Job accepted (HTTP 202). Flat ProcessingJobStartResponse; may include jobs[] when\
\ auto-split."
content:
application/json:
schema:
$ref: "#/components/schemas/ProcessingJobStartResponse"
example:
id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
status: pending
total_products: 2
processing_type: full
'400':
description: Invalid JSON, invalid raw_product_id, empty ids, or processing.ClientError / LogAndError.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: could not start processing job
'402':
description: Plan gate blocked start. Special shape from handleStartProcessingJob (error string
2026-08-17 21:20:45 +02:00
+ code + upgrade_url) — not the coded envelope.
content:
application/json:
schema:
$ref: "#/components/schemas/PlanGateError"
example:
error: Insufficient credits
code: insufficient_credits
upgrade_url: /pricing
"429": { $ref: "#/components/responses/TooManyRequests" }
'500':
description: River enqueue failed after job row create.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: enqueue failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/process/{id}:
get:
tags:
- Processing
summary: Processing job status (raw_product_ids surface)
description: |
2026-08-17 21:20:45 +02:00
Public processing job JSON: status, counts, timestamps, processing_type.
2026-08-17 21:56:09 +02:00
Omits current_step, step_progress, and company_id. Completed jobs still include
items[] with the same AI HTML description contract as GET /products/process/{id}
(category formula body, not a one- or two-sentence blurb).
parameters:
- $ref: "#/components/parameters/ID"
responses:
'200':
description: "Flat ProcessingJob when GetJob succeeds for this company. Completed jobs include items[]."
content:
application/json:
schema:
$ref: "#/components/schemas/ProcessingJob"
examples:
processing:
summary: In progress
value:
id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
status: processing
total_products: 25
processed_products: 8
processing_type: full
completed:
summary: Finished with processed products
value:
id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
status: completed
total_products: 1
processed_products: 1
processing_type: full
items:
- ean: '0123456789012'
status: processed
2026-08-17 21:20:45 +02:00
name: Acme Wireless Earbuds ANC Black
category: Headphones
2026-08-17 21:56:09 +02:00
description: "<h1>Acme Wireless Earbuds ANC Black</h1><p>Acme Wireless Earbuds ANC Black are in-ear headphones for commuting and desk work. Active noise cancellation, a secure fit, and a charging case cover a full workday of listening. The compact buds sit flush enough for glasses and hats, and the case fits a jacket pocket.</p><p>Pair over Bluetooth and switch between a phone and a laptop with the onboard controls. Transparency mode lets nearby voices through without removing the buds. The default ear-tip set covers common sizes; swap tips if bass feels weak or the seal slips during a walk.</p><h2>In the box</h2><ul><li>Wireless earbuds (left and right)</li><li>Charging case with USB-C cable</li><li>Three ear-tip sizes</li><li>Quick-start guide</li></ul><p>Charge the case before first use. Keep the charging contacts dry and store the buds in the case so they sleep and recharge between sessions. For replacement tips or a spare case, quote the product name and the EAN on the box.</p>"
total_items: 1
'400':
description: Path id is not a UUID.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid id
'404':
description: Job not found for this company.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/process/{id}/cancel:
post:
tags:
- Processing
summary: Cancel processing job
parameters:
- $ref: "#/components/parameters/ID"
responses:
'200':
description: Cancel/terminate acknowledged. Returns the updated job object when CancelJob succeeds.
content:
application/json:
schema:
$ref: "#/components/schemas/ProcessingJob"
example:
id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
status: cancelled
total_products: 25
'400':
description: Invalid id, or processing.ClientError / LogAndError (job not cancellable).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: could not cancel job
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'404':
description: Resource id not found for this API-key company (or wrong tenant).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/process/{id}/terminate:
post:
tags:
- Processing
summary: Terminate processing job (alias of cancel)
2026-08-17 21:20:45 +02:00
description: "Naming alias of POST /process/{id}/cancel."
parameters:
- $ref: "#/components/parameters/ID"
responses:
'200':
description: Cancel/terminate acknowledged. Returns the updated job object when CancelJob succeeds.
content:
application/json:
schema:
$ref: "#/components/schemas/ProcessingJob"
example:
id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
status: cancelled
total_products: 25
'400':
description: Invalid id, or processing.ClientError / LogAndError (job not cancellable).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: could not cancel job
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'500':
description: Unexpected server error for this operation (handler internal_error / flat Error).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: internal error
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'404':
description: Resource id not found for this API-key company (or wrong tenant).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/process/{id}/retry:
post:
tags:
- Processing
summary: Retry a failed or cancelled processing job
parameters:
- $ref: "#/components/parameters/ID"
responses:
'202':
description: Retry accepted (HTTP 202). Flat job after RetryJob + enqueue.
content:
application/json:
schema:
$ref: "#/components/schemas/ProcessingJob"
example:
id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
status: pending
total_products: 25
'400':
description: Invalid id or job not retryable (ClientError / LogAndError).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: could not retry job
"429": { $ref: "#/components/responses/TooManyRequests" }
'500':
description: Enqueue failed after retry.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: enqueue failed
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'403':
description: Insufficient role or wrong company. On API-key routes, cross-tenant ids usually
return 404 instead. requireCompanyAdmin returns flat 'admin required' for session non-admin
callers (API keys use role api and pass).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'404':
description: Resource id not found for this API-key company (or wrong tenant).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'422':
description: Validation failed. Current public v1 handlers emit these cases as HTTP 400 (flat
or coded). 422 is documented for clients that expect an explicit validation status; body matches
2026-08-17 21:20:45 +02:00
FlatAPIError or CodedAPIError.
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error()
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: v1Err coded
value:
error:
code: validation_error
message: invalid json
/team/{userID}:
parameters:
- name: userID
in: path
required: true
schema:
type: string
format: uuid
description: Membership user id within the selected company
patch:
tags:
- Team
summary: Update team member role
description: |
Company admin or platform admin. Demoting the last active admin returns 409.
Requires session cookie + CSRF double-submit (X-CSRF-Token).
Dashboard surface under /api (not the public /api/v1 API-key base).
security:
- SessionCookie: []
CSRFHeader: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- role
properties:
role:
type: string
enum:
- admin
- member
description: Normalized to admin|member (case-insensitive input accepted)
example:
role: member
responses:
'200':
description: Role updated (or unchanged)
content:
application/json:
schema:
type: object
required:
- status
- role
- user_id
properties:
status:
type: string
example: ok
role:
type: string
enum:
- admin
- member
user_id:
type: string
format: uuid
example:
status: ok
role: member
user_id: 4f3c2b1a-0e9d-4c8b-7a6f-5e4d3c2b1a09
'400':
description: Invalid request for this route (HTTP 400).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid json
'403':
description: Forbidden (not company/platform admin)
'404':
description: Resource not found (HTTP 404).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: not found
'409':
description: Cannot demote the last admin
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: cannot demote the last admin
'401':
description: Missing or invalid API key. RequireAPIKey returns this when Authorization Bearer
2026-08-17 21:20:45 +02:00
and X-API-Key are both absent/empty or AuthenticateAPIKey returns ErrInvalidAPIKey. Coded
envelope (not a flat string).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
'422':
description: Validation failed. Dashboard routes may use this status; prefer reading the message.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: validation failed
'500':
description: Unexpected server error (HTTP 500).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: list failed
/admin/emails/set-password:
post:
tags:
- Admin
summary: Re-issue set-password invites
description: |
Platform admin only. Prefers durable invite reissue (ReissueSetPasswordInvite);
falls back to HMAC set-password tokens when the user has no active membership.
2026-08-17 21:20:45 +02:00
Skips synthetic placeholder emails. Rate-limited per admin.
When SMTP is disabled and a single user_id is provided, the response may include
a one-time token for local/staging link copy (never logs email/token).
Dashboard surface under /api (not the public /api/v1 API-key base).
security:
- SessionCookie: []
CSRFHeader: []
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
user_id:
type: string
format: uuid
description: |
Target user UUID. When omitted, bulk-targets users needing a password (capped).
example:
user_id: 4f3c2b1a-0e9d-4c8b-7a6f-5e4d3c2b1a09
responses:
'200':
description: Issue/send summary
content:
application/json:
schema:
type: object
required:
- sent
- issued
- skipped
- smtp_enabled
- mode
properties:
sent:
type: integer
issued:
type: integer
skipped:
type: integer
skipped_synthetic:
type: integer
skipped_ineligible:
type: integer
skipped_rate_limited:
type: integer
skipped_send:
type: integer
smtp_enabled:
type: boolean
mode:
type: string
example: invite
token:
type: string
description: Present only for single-user reissue when SMTP is off
example:
sent: 1
issued: 1
skipped: 0
skipped_synthetic: 0
skipped_ineligible: 0
skipped_rate_limited: 0
skipped_send: 0
smtp_enabled: true
mode: invite
'401':
description: Unauthorized — missing session or privilege for this dashboard route.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: unauthorized
'429':
description: Rate limit exceeded for this admin action. May include Retry-After.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: Too many requests
'503':
description: Dependency unavailable (for example mailer down).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: mailer unavailable
'400':
description: Invalid request for this route (HTTP 400).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: invalid json
'403':
description: Forbidden for this route (HTTP 403).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: admin required
'409':
description: Conflict with current resource state.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: conflict
'422':
description: Validation failed. Dashboard routes may use this status; prefer reading the message.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: validation failed
'500':
description: Unexpected server error (HTTP 500).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: list failed
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: API key (dk_...)
description: |
Preferred scheme for /api/v1. Value is the raw company API key
(prefix dk_...), not a JWT. Example header:
Authorization: Bearer dk_your_key
Create keys in Settings -> API keys. RapiDoc Try-it: paste the key, or
use "Use my API key" when logged into the docs page. Full keys are never
published in this YAML.
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
description: |
Alternate scheme for /api/v1. Same company API key as BearerAuth, sent
as header X-API-Key: dk_your_key. When both Authorization Bearer and
X-API-Key are present, Bearer wins.
SessionCookie:
type: apiKey
in: cookie
name: descrybe_session
description: Dashboard session cookie only (SESSION_COOKIE_NAME; default descrybe_session). Not
valid for /api/v1 public routes.
CSRFHeader:
type: apiKey
in: header
name: X-CSRF-Token
description: Dashboard CSRF double-submit header (must match descrybe_csrf cookie). Not used by
/api/v1 API-key routes.
parameters:
ID:
in: path
name: id
required: true
schema:
type: string
format: uuid
description: |
Resource UUID for this path (product, attribute, feed, export feed, or processing job).
Format: UUID string (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). Required.
Limit:
in: query
name: limit
schema:
type: integer
default: 50
maximum: 200
minimum: 1
description: |
2026-08-17 21:20:45 +02:00
Page size for other list endpoints. Integer, default 50, minimum 1, maximum 200.
PublicLimit:
in: query
name: limit
schema:
type: integer
default: 25
maximum: 100
minimum: 1
description: |
2026-08-17 21:20:45 +02:00
Page size for public list endpoints. Integer, default 25, minimum 1, maximum 100.
Page:
in: query
name: page
schema:
type: integer
default: 1
minimum: 1
description: |
1-based page index for offset/page pagination. Integer, default 1, minimum 1.
Offset:
in: query
name: offset
schema:
type: integer
default: 0
description: Offset pagination (ignored when cursor or after_id is set)
Cursor:
in: query
name: cursor
schema:
type: string
description: Opaque keyset cursor from next_cursor (preferred for deep pages)
AfterID:
in: query
name: after_id
schema:
type: string
format: uuid
description: Keyset bookmark by product id; cursor wins when both are set
requestBodies:
2026-08-17 21:20:45 +02:00
StartProcessByEAN:
required: true
description: |
2026-08-17 21:20:45 +02:00
Prefer items[].ean. Same handler also accepts raw_product_ids
when items is omitted. Sending neither returns validation_error.
raw_product_ids must be raw_products.id values (see GET /products data[].raw_product_id).
Do not pass PresentProduct.id / processed_products.id.
content:
application/json:
schema:
type: object
properties:
items:
type: array
minItems: 1
2026-08-17 21:20:45 +02:00
description: Primary body — required unless raw_product_ids is set
items:
type: object
required:
- ean
properties:
ean:
type: string
description: GTIN / EAN barcode digits (required). Typically 8-14 characters.
category_unique_id:
type: string
description: Optional category unique_id; forces categorization when set
title:
type: string
description: Optional product title seed for enrichment
description:
type: string
2026-08-17 21:56:09 +02:00
description: |
Optional source/seed text. AI enhance rewrites this into the
category description-formula HTML body. It is not the final
poll payload and does not cap length to a short summary.
specifications:
type: array
description: Optional key/value specification pairs for attribute hints
items:
type: object
properties:
key:
type: string
description: Specification attribute key
value:
type: string
description: Specification attribute value
search:
type: string
description: Optional search keywords passed into enrichment context
main_image:
type: string
format: uri
description: Primary product image URL
more_images:
description: Additional image URLs as a single string or string array
oneOf:
- type: string
- type: array
items:
type: string
image_url:
type: string
format: uri
description: Alternate primary image URL field (alias of main_image)
additional_image_urls:
description: Extra image URLs as a single string or string array
oneOf:
- type: string
- type: array
items:
type: string
image_link:
type: string
format: uri
description: Google-style primary image_link URL
additional_image_link:
description: Google-style additional image links (string or array)
oneOf:
- type: string
- type: array
items:
type: string
raw_product_ids:
type: array
items:
type: string
format: uuid
minItems: 1
description: |
Alternate body — existing raw_products.id UUIDs (items takes precedence).
Not PresentProduct.id. Use GET /products data[].raw_product_id.
processing_type:
description: |
2026-08-17 21:56:09 +02:00
full (default) runs the whole pipeline including AI enhance, which
writes formula-driven HTML descriptions (not a one- or two-sentence
blurb). Named steps category|title|description|attributes (string or
array of those steps); dual-mode also accepts v2 dashboard types such as
normalize_only, enhance_only, attributes_only, eprel_only, categorize_only
(ParseV1ProcessingType). Alias processingType accepted when values match.
2026-08-17 21:56:09 +02:00
The description step still emits the category formula HTML body.
oneOf:
- type: string
enum:
- full
- category
- title
- description
- attributes
- normalize_only
- enhance
- enhance_only
- enhance-only
- attributes_only
- specs
- specifications
- eprel
- eprel_only
- categorize
- categorize_only
- categorize_enhance
- type: array
items:
type: string
enum:
- category
- title
- description
- attributes
processingType:
description: CamelCase alias of processing_type (must match if both set)
oneOf:
- type: string
- type: array
items:
type: string
processing_types:
type: array
items:
type: string
description: Dashboard fine-grained steps; used when processing_type omitted
examples:
by_ean:
2026-08-17 21:20:45 +02:00
summary: "items[].ean (preferred)"
value:
items:
- ean: '4548736132174'
title: WH-1000XM5 Sony WH-1000XM5 Black
category_unique_id: electronics
main_image: https://images.example.com/products/wh1000xm5-black.jpg
more_images:
- https://images.example.com/products/wh1000xm5-black-side.jpg
- ean: 0194252092942
title: Anker PowerLine III USB-C to USB-C 2m
processing_type: full
by_raw_ids:
summary: Alternate raw_product_ids on same path
value:
raw_product_ids:
- 2c5ea4c0-4067-4e44-8c5a-9a8b7c6d5e4f
processing_type: normalize_only
StartProcessByRawIDs:
required: true
description: |
Dashboard-style start body — existing raw product UUIDs only (no items[].ean).
Used by POST /process. Returns flat ProcessingJob JSON with HTTP 202.
IDs must be raw_products.id (GET /products data[].raw_product_id), never PresentProduct.id.
content:
application/json:
schema:
type: object
required:
- raw_product_ids
properties:
raw_product_ids:
type: array
items:
type: string
format: uuid
minItems: 1
description: |
raw_products.id UUIDs to enqueue. Non-empty array of UUID strings (required).
Do not pass processed product list id values.
processing_type:
type: string
description: |
2026-08-17 21:20:45 +02:00
full (default) or a named dashboard step (category, title, description,
attributes, normalize_only, enhance_only, …).
default: full
example: full
processing_types:
type: array
items:
type: string
description: Optional fine-grained steps (dashboard); StartJob uses processing_type
example:
raw_product_ids:
- 2c5ea4c0-4067-4e44-8c5a-9a8b7c6d5e4f
- 550e8400-e29b-41d4-a716-446655440001
processing_type: full
CreateCategory:
required: true
description: |
Create a catalog category. Requires name and unique_id. parent_id is accepted as an
alias of parent_unique_id.
content:
application/json:
schema:
type: object
required:
- name
- unique_id
properties:
name:
type: string
description: Display name shown in the catalog UI (required)
unique_id:
type: string
description: |
Stable slug identifier (e.g. headphones). Lowercase letters, digits,
underscores/hyphens. Required and unique within the company.
parent_unique_id:
type: string
nullable: true
description: Parent category unique_id, or null/omit for a root category
parent_id:
type: string
nullable: true
2026-08-17 21:20:45 +02:00
description: Alias of parent_unique_id
description:
type: string
nullable: true
description: Optional human-readable category description
example:
name: Headphones
unique_id: electronics_audio_headphones
parent_id: electronics_audio
description: Over-ear and in-ear headphones
CreateAttribute:
required: true
description: |
Create a catalog attribute and link it to a category. Requires name, attribute_key,
value_type, and category_unique_id.
content:
application/json:
schema:
type: object
required:
- name
- attribute_key
- value_type
- category_unique_id
properties:
name:
type: string
description: Human-readable attribute label (required)
attribute_key:
type: string
description: |
Stable machine key (e.g. battery_life_hours). Snake_case preferred. Required.
value_type:
type: string
enum:
- string
- number
- list
- multiselect
description: |
Value shape. string, number, list, or multiselect (required).
category_unique_id:
type: string
description: Category unique_id slug to attach this attribute to (required)
unit:
type: string
nullable: true
description: Optional unit label (e.g. W, cm)
example:
type: string
nullable: true
description: Sample value for docs/UI hints
required:
type: boolean
default: false
description: When true, products in this category should supply the attribute
parent_key:
type: string
nullable: true
description: Optional parent attribute_key for nested/grouped attributes
example:
name: Battery Life
attribute_key: battery_life_hours
value_type: number
category_unique_id: electronics_audio_headphones
unit: h
required: false
example: '65'
schemas:
HealthStatus:
type: object
required:
- status
- service
- maintenance
- read_only
properties:
status:
type: string
example: ok
description: ok for liveness; ready/not_ready on /readyz
service:
type: string
example: api
maintenance:
type: boolean
description: When true
API is in maintenance mode: null
read_only:
type: boolean
description: When true
mutating writes are rejected: null
hypercare:
type: boolean
description: When true, tenant hypercare report-missing CTA is shown (P1-17)
checks:
type: object
additionalProperties:
type: string
description: Present on /readyz (e.g. database ok|fail|unavailable)
error:
type: string
description: Present on /readyz when not ready (safe public message)
ProductListResponse:
type: object
required: [data, meta]
properties:
data:
type: array
items:
$ref: "#/components/schemas/PresentProduct"
meta:
type: object
required:
- page
- limit
- total
properties:
page:
type: integer
limit:
type: integer
total:
type: integer
totalPages:
type: integer
ProductQualityListResponse:
type: object
required:
- data
- meta
properties:
data:
type: array
items:
type: object
properties:
id:
type: string
format: uuid
product_id:
type: string
name:
type: string
quality_score:
type: integer
quality_grade:
type: string
quality_checks:
type: object
additionalProperties: true
meta:
type: object
required:
- page
- limit
- total
properties:
page:
type: integer
limit:
type: integer
total:
type: integer
PresentProduct:
type: object
properties:
id:
type: string
format: uuid
description: |
processed_products.id for this list row. Do not send as raw_product_ids —
use raw_product_id instead.
product_id:
type: string
example: SONY-WH1000XM5-B
name:
type: string
nullable: true
category:
type: string
nullable: true
status:
type: string
example: completed
raw_product_id:
type: string
format: uuid
nullable: true
description: |
raw_products.id for dual-mode POST /products/process and POST /process bodies.
Prefer this over id when starting jobs by UUID.
feed_id:
type: string
format: uuid
nullable: true
quality_score:
type: integer
quality_grade:
type: string
example: C
created_at:
type: string
format: date-time
nullable: true
updated_at:
type: string
format: date-time
nullable: true
ProcessedProduct:
type: object
properties:
id:
type: string
format: uuid
product_id:
type: string
example: SONY-WH1000XM5-B
name:
type: string
processed_name:
type: string
category:
type: string
description:
type: string
2026-08-17 21:56:09 +02:00
description: |
Product body. After processing this is the AI HTML description
(category formula), not a one- or two-sentence summary.
processed_description:
type: string
2026-08-17 21:56:09 +02:00
description: |
Enriched HTML body from AI enhance. Same contract as ProcessItem.description.
status:
type: string
example: completed
raw_product_id:
type: string
format: uuid
feed_id:
type: string
format: uuid
gtin:
type: string
attributes:
type: object
additionalProperties: true
processed_attributes:
type: object
additionalProperties: true
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
CategoryListResponse:
type: object
required:
- categories
- total
- limit
- offset
properties:
categories:
type: array
items:
type: object
total:
type: integer
limit:
type: integer
offset:
type: integer
2026-08-17 21:20:45 +02:00
EnvelopePaginationMeta:
type: object
required:
- page
- limit
- total
properties:
page:
type: integer
limit:
type: integer
total:
type: integer
totalPages:
type: integer
2026-08-17 21:20:45 +02:00
CategoriesEnvelope:
type: object
required:
- data
- meta
properties:
data:
type: array
items:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/EnvelopeCategory"
meta:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/EnvelopePaginationMeta"
EnvelopeCategory:
type: object
properties:
id:
type: string
format: uuid
unique_id:
type: string
name:
type: string
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
CategoryDetail:
type: object
2026-08-17 21:20:45 +02:00
description: "Flat category row from GET/PATCH /categories/{id} (not a data envelope)."
properties:
id:
type: string
format: uuid
name:
type: string
unique_id:
type: string
parent_unique_id:
type: string
nullable: true
path:
type: string
nullable: true
level:
type: integer
position:
type: integer
is_active:
type: boolean
description:
type: string
nullable: true
title_template:
nullable: true
2026-08-17 21:56:09 +02:00
description: |
Title formula for AI enhance. Controls how processed name is built
(type, brand, model, and other slots).
description_template:
nullable: true
2026-08-17 21:56:09 +02:00
description: |
Description formula for AI enhance. Ordered sections (h1/h2/h3/h4, p, ul)
with per-section instructions. Controls HTML structure and length of
processed items[].description — short, long, or very large copy.
Not a one- or two-sentence cap.
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
2026-08-17 21:20:45 +02:00
CategoryCreateEnvelope:
type: object
required:
- data
properties:
data:
type: object
required:
- id
- unique_id
- name
properties:
id:
type: string
format: uuid
unique_id:
type: string
name:
type: string
2026-08-17 21:20:45 +02:00
AttributesEnvelope:
type: object
required:
- data
- meta
properties:
data:
type: array
items:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/EnvelopeAttribute"
meta:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/EnvelopePaginationMeta"
EnvelopeAttribute:
type: object
properties:
id:
type: string
format: uuid
key:
type: string
name:
type: string
type:
type: string
unit:
type: string
nullable: true
required:
type: boolean
category_unique_id:
type: string
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
AttributeDetail:
type: object
2026-08-17 21:20:45 +02:00
description: "Flat attribute row from PATCH /attributes/{id} (not a data envelope)."
properties:
id:
type: string
format: uuid
attribute_key:
type: string
name:
type: string
value_type:
type: string
enum: [string, number, list, multiselect]
unit:
type: string
nullable: true
example:
type: string
nullable: true
parent_key:
type: string
nullable: true
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
2026-08-17 21:20:45 +02:00
AttributeCreateEnvelope:
type: object
required:
- data
properties:
data:
type: object
properties:
id:
type: string
format: uuid
key:
type: string
name:
type: string
type:
type: string
unit:
type: string
nullable: true
category_unique_id:
type: string
required:
type: boolean
2026-08-17 21:20:45 +02:00
SuccessMessage:
type: object
required:
- data
properties:
data:
type: object
required:
- message
properties:
message:
type: string
FeedListResponse:
type: object
required:
- data
- meta
properties:
data:
type: array
items:
$ref: "#/components/schemas/PresentFeed"
meta:
type: object
required:
- page
- limit
- total
properties:
page:
type: integer
limit:
type: integer
total:
type: integer
description: All matching feeds
offset:
type: integer
active_total:
type: integer
description: Feeds with status active (truly syncing)
mapped_total:
type: integer
description: Feeds with status mapped (fields saved, not activated)
FeedGetResponse:
type: object
required:
- data
properties:
data:
$ref: "#/components/schemas/PresentFeed"
FeedCreateResponse:
type: object
required:
- data
properties:
data:
$ref: "#/components/schemas/PresentFeed"
FeedSyncResponse:
type: object
required:
- data
properties:
data:
type: object
required:
- jobId
properties:
jobId:
type: string
format: uuid
job_id:
type: string
format: uuid
description: Dual-support snake_case alias
PresentFeed:
type: object
properties:
id:
type: string
format: uuid
name:
type: string
url:
type: string
nullable: true
item_path:
type: string
is_active:
type: boolean
product_count:
type: integer
status:
type: string
last_synced:
type: string
format: date-time
nullable: true
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
feed_type:
type: string
description: Dual-support v2 field
sync_interval_minutes:
type: integer
description: Dual-support v2 field
last_synced_at:
type: string
format: date-time
nullable: true
options:
type: object
additionalProperties: true
FeedMappings:
type: object
properties:
id:
type: string
format: uuid
description: Absent when no mappings row exists yet
version:
type: integer
mappings:
description: Field mapping document (object or array). Empty array when none saved.
oneOf:
- type: object
additionalProperties: true
- type: array
items:
type: object
additionalProperties: true
SchemaExtractResult:
type: object
required:
- feed_id
- format
- fields
- sample_rows
properties:
feed_id:
type: string
format: uuid
format:
type: string
enum:
- xml
- csv
suggested_item_path:
type: string
item_path:
type: string
fields:
type: array
items:
type: object
properties:
path:
type: string
field_name:
type: string
data_type:
type: string
sample_values:
type: array
items:
type: string
unique_values_count:
type: integer
suggested_target:
type: string
sample_rows:
type: integer
preview:
type: string
preview_truncated:
type: boolean
ExportFeedDetail:
type: object
description: Flat export_feeds row from GET/PATCH/PUT template handlers (not presentV1ExportFeed
/ data envelope).
properties:
id:
type: string
format: uuid
name:
type: string
source_feed_id:
type: string
format: uuid
nullable: true
format:
type: string
enum:
- xml
- csv
public_token:
type: string
template:
type: object
additionalProperties: true
nullable: true
filters:
type: object
additionalProperties: true
nullable: true
is_active:
type: boolean
last_generated_at:
type: string
format: date-time
nullable: true
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
StatusOK:
type: object
required:
- status
properties:
status:
type: string
example: ok
FeedDeleted:
type: object
required:
- id
- deleted
properties:
id:
type: string
format: uuid
deleted:
type: boolean
PreparedCampaign:
type: object
properties:
preset_id:
type: string
enum:
- black_friday
- christmas
name:
type: string
start_date:
type: string
format: date
end_date:
type: string
format: date
year:
type: integer
export_feed_id:
type: string
format: uuid
export_feed_name:
type: string
created:
type: boolean
description: True only on newly prepared campaigns
PreparedCampaignEnvelope:
type: object
required:
- data
properties:
data:
$ref: "#/components/schemas/PreparedCampaign"
MarketingCalendar:
type: object
required:
- year
- presets
- prepared
properties:
year:
type: integer
presets:
type: array
items:
type: object
properties:
id:
type: string
name:
type: string
description:
type: string
start_date:
type: string
format: date
end_date:
type: string
format: date
year:
type: integer
prepared:
type: array
items:
$ref: "#/components/schemas/PreparedCampaign"
ProcessingJob:
type: object
properties:
id:
type: string
format: uuid
company_id:
type: string
format: uuid
status:
type: string
example: pending
total_products:
type: integer
processed_products:
type: integer
processing_type:
type: string
current_step:
type: string
step_progress:
type: array
items:
type: object
properties:
step:
type: string
status:
type: string
note:
type: string
error:
type: string
nullable: true
started_at:
type: string
format: date-time
nullable: true
completed_at:
type: string
format: date-time
nullable: true
created_at:
type: string
format: date-time
items:
type: array
description: Present when status is completed — processed product payload (additive)
items:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/ProcessItem"
total_items:
type: integer
description: Present with items when status is completed
jobs:
type: array
items:
$ref: "#/components/schemas/ProcessingJob"
description: Present when StartJob auto-splits into multiple jobs
sibling_job_ids:
type: array
items:
type: string
format: uuid
job_count:
type: integer
total_products_queued:
type: integer
ProcessingJobStartResponse:
description: Single Job object, or Job plus split metadata (jobs, sibling_job_ids, …)
allOf:
- $ref: "#/components/schemas/ProcessingJob"
PlanGateError:
type: object
properties:
error:
type: string
code:
type: string
enum:
- insufficient_credits
- product_limit
- ai_requires_upgrade
- eprel_requires_upgrade
- plan_gate
upgrade_url:
type: string
example: /pricing
FlatAPIError:
type: object
required:
- error
properties:
error:
type: string
2026-08-17 21:20:45 +02:00
CodedAPIError:
type: object
required:
- error
properties:
error:
type: object
required:
- code
- message
properties:
code:
type: string
example: validation_error
message:
type: string
requestId:
type: string
2026-08-17 21:20:45 +02:00
ProcessStartEnvelope:
type: object
required:
- data
properties:
data:
type: object
required:
- process_id
- message
properties:
process_id:
type: string
format: uuid
2026-08-17 09:33:07 +02:00
status:
type: string
description: |
Job lifecycle status on enqueue. Always pending (or processing if the
worker already picked it up). Never COMPLETED on start — poll GET
/products/process/{id} for completion.
example: pending
message:
type: string
total_items:
type: integer
2026-08-17 09:33:07 +02:00
description: Number of products accepted into the job (enqueue size).
processed_items:
type: integer
2026-08-17 09:33:07 +02:00
description: |
Count of items that finished processing. Always 0 on start while the
job is pending/processing; increments as the worker completes products.
example: 0
job_count:
type: integer
description: Present when StartJob auto-splits
sibling_job_ids:
type: array
items:
type: string
format: uuid
total_products_queued:
type: integer
errors:
type: array
items:
type: string
description: Per-item upsert failures when some EANs still queued
2026-08-17 21:20:45 +02:00
ProcessStatusEnvelope:
type: object
required:
- data
properties:
data:
type: object
required:
- status
- process_id
properties:
status:
type: string
description: Uppercase job status (COMPLETED, FAILED, PENDING, PROCESSING, …)
example: COMPLETED
process_id:
type: string
format: uuid
processing_type:
description: Echo of requested type (string or step array)
oneOf:
- type: string
- type: array
items:
type: string
items:
type: array
description: Present when status is COMPLETED
items:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/ProcessItem"
total_items:
type: integer
processed_at:
type: string
format: date-time
message:
type: string
error:
type: string
description: Present when status is FAILED
created_at:
type: string
format: date-time
started_at:
type: string
format: date-time
nullable: true
2026-08-17 21:20:45 +02:00
ProcessItem:
2026-08-16 16:57:36 +02:00
type: object
description: |
2026-08-17 21:20:45 +02:00
One COMPLETED process line (public contract). Successful items
2026-08-17 01:30:28 +02:00
expose category as the human-readable display name (category_id holds
2026-08-17 21:56:09 +02:00
categories.unique_id), a description string that is generated HTML
product copy (h1/h2/h3/h4, p, ul — never a JSON array and never a one-
or two-sentence slogan), optional SEO meta_title / meta_description
(plain text; some companies omit these fields), optional eprel object
or null, clean attributes, and images.
2026-08-17 09:33:07 +02:00
Product display name is name (primary). title is omitted when identical to name.
Internal UUIDs (id, processed_product_id, raw_product_id) are omitted from this
public shape — use catalog APIs when a product UUID is required.
Property order prefers human-readable fields first (ean, name, category,
description, attributes, images, eprel).
2026-08-16 16:57:36 +02:00
required:
- ean
properties:
ean:
2026-08-16 16:57:36 +02:00
type: string
category:
2026-08-17 01:30:28 +02:00
type: string
nullable: true
description: |
Human-readable category display name (same value as category_name when
resolved). Prefer this for UI. Machine unique_id is category_id.
category_id:
2026-08-16 16:57:36 +02:00
type: string
nullable: true
description: |
categories.unique_id for the assigned category. Opaque string — may be
numeric (e.g. "28" or "50") or slug-like. Not a URL path and not a UUID.
category_name:
2026-08-16 16:57:36 +02:00
type: string
nullable: true
2026-08-17 01:30:28 +02:00
description: |
Human-readable category display name (mirrors category when both are set).
2026-08-17 09:33:07 +02:00
name:
2026-08-16 16:57:36 +02:00
type: string
nullable: true
description: |
2026-08-17 09:33:07 +02:00
Product display name (processed title). Primary field for clients.
title:
2026-08-16 16:57:36 +02:00
type: string
nullable: true
description: |
2026-08-17 21:20:45 +02:00
Optional alias of name. Omitted when identical to name.
meta_title:
2026-08-16 16:57:36 +02:00
type: string
nullable: true
description: |
2026-08-17 21:20:45 +02:00
Optional SEO title. When present, filled from processing meta or
synthesized from name / category. Some companies omit this field.
meta_description:
2026-08-16 16:57:36 +02:00
type: string
nullable: true
description: |
2026-08-17 21:20:45 +02:00
Optional SEO description (word-safe truncate). Distinct from body
description when possible. Some companies omit this field.
description:
2026-08-16 16:57:36 +02:00
type: string
nullable: true
description: |
2026-08-17 21:56:09 +02:00
Generated product body as one HTML string — never a JSON array and
never a one- or two-sentence slogan. AI enhance follows the assigned
category description_template (description formula): ordered sections
of type h1, h2, h3, h4, p, and ul, each with its own instructions.
Output can be a short paragraph, several 100-word blocks, or a very
large multi-section article. When no formula is set, expect 1-3
factual paragraphs with limited HTML. Optional meta_description stays
plain SEO text and is not a substitute for this field.
2026-08-16 16:57:36 +02:00
attributes:
type: object
additionalProperties: true
nullable: true
description: |
Characteristic catalog attributes only (brand, model, dims, warranty, …).
Core fields and eprel_* keys are not duplicated here.
main_image:
2026-08-16 16:57:36 +02:00
type: string
nullable: true
more_images:
2026-08-16 16:57:36 +02:00
type: array
items:
type: string
nullable: true
eprel:
2026-08-16 16:57:36 +02:00
nullable: true
type: object
description: |
EU energy label payload when EPREL data is available; otherwise null.
Prefer this object over raw eprel_* keys inside attributes.
Live shape keys: id, label, pdf, energy_class, energy_scale.
properties:
id:
type: string
description: EPREL product registration id (same as eprel_id).
label:
2026-08-16 16:57:36 +02:00
type: string
pdf:
2026-08-16 16:57:36 +02:00
type: string
energy_class:
2026-08-16 16:57:36 +02:00
type: string
energy_scale:
2026-08-16 16:57:36 +02:00
type: string
status:
2026-08-16 16:57:36 +02:00
type: string
description: Per-item outcome — processed on success; not_found / failed / cancelled otherwise
example: processed
error:
2026-08-16 16:57:36 +02:00
type: string
examples:
ProcessingJobAccepted:
summary: Single processing job accepted (POST /process)
value:
id: 8f14e45f-ceea-467a-9e5d-9c6b0e8f3a21
company_id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
status: pending
total_products: 2
processed_products: 0
processing_type: full
current_step: category
step_progress:
- step: category
status: pending
- step: title
status: pending
- step: description
status: pending
- step: attributes
status: pending
created_at: '2026-08-04T09:59:50Z'
ProcessingJobSplitAccepted:
summary: Auto-split batch (POST /process)
value:
id: 8f14e45f-ceea-467a-9e5d-9c6b0e8f3a21
company_id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
status: pending
total_products: 50
processed_products: 0
processing_type: full
current_step: category
created_at: '2026-08-04T09:59:50Z'
jobs:
- id: 8f14e45f-ceea-467a-9e5d-9c6b0e8f3a21
status: pending
total_products: 50
- id: 1b4e28ba-2fa1-4d3a-9c6e-7f8a9b0c1d2e
status: pending
total_products: 50
sibling_job_ids:
- 1b4e28ba-2fa1-4d3a-9c6e-7f8a9b0c1d2e
job_count: 2
total_products_queued: 100
ProcessingJobRunning:
summary: "Processing job in progress (GET /process/{id})"
value:
id: 8f14e45f-ceea-467a-9e5d-9c6b0e8f3a21
company_id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
status: processing
total_products: 25
processed_products: 8
processing_type: full
current_step: title
step_progress:
- step: category
status: done
- step: title
status: running
- step: description
status: pending
- step: attributes
status: pending
started_at: '2026-08-04T10:00:00Z'
created_at: '2026-08-04T09:59:50Z'
2026-08-17 21:20:45 +02:00
ProcessCompletedExample:
summary: "Completed poll item with category, description, and eprel"
value:
data:
status: COMPLETED
process_id: 8f14e45f-ceea-467a-9e5d-9c6b0e8f3a21
processing_type: full
items:
2026-08-16 16:57:36 +02:00
- ean: '8606019604493'
status: processed
2026-08-17 01:30:28 +02:00
category: Cookers
category_id: '50'
2026-08-16 16:57:36 +02:00
category_name: Cookers
name: VOX electric cooker EHT 6020 WG
2026-08-17 21:56:09 +02:00
description: "<h1>VOX electric cooker EHT 6020 WG</h1><p>The VOX EHT 6020 WG is a free-standing electric cooker for everyday household cooking. Four ceramic hobs and a 65 L fan oven let you boil, fry, and bake at the same time, while energy class A helps keep running costs down. The 60 cm width fits a standard kitchen niche. An enamel oven cavity and removable inner door glass are designed for cleaning after heavy use.</p><p>Run sauces or pasta on the hob while the oven handles a roast or tray bake. Heat is even across the ceramic zones, and residual-heat indicators reduce the chance of accidental contact after cooking. Fan mode suits multi-level baking; conventional heat remains available for dishes that prefer still air. A full-width grill finishes cheese, gratins, and toast.</p><h2>Key features</h2><ul><li>Four electric ceramic hobs with residual-heat indicators</li><li>65 L fan oven plus grill, energy class A</li><li>60 cm free-standing body (0.6 m wide, 0.85 m high, 0.6 m deep)</li><li>Enamel cavity and removable inner door glass</li><li>60-month manufacturer warranty</li></ul><p>Install in a standard 60 cm cooker slot with the dedicated electric supply shown on the rating plate. Leave rear clearance for ventilation and use a heat-resistant worktop next to the hob. This product is for household kitchens, not commercial catering loads.</p><p>Use cookware that matches each zone so energy is not wasted around the pan. Wipe the ceramic surface regularly and empty crumbs from the oven floor to preserve appearance and efficiency. For spare parts, quote model EHT 6020 WG and the EAN on the rating plate.</p>"
attributes:
2026-08-16 16:57:36 +02:00
brand: Vox
product_model: EHT6020WG
width: 0.6 m
height: 0.85 m
depth: 0.6 m
weight: 42.81 kg
warranty: 60 months
main_image: https://images.example.com/products/vox-eht6020wg.jpg
more_images:
2026-08-16 16:57:36 +02:00
- https://images.example.com/products/vox-eht6020wg-side.jpg
eprel:
id: "1234567"
label: https://eprel.ec.europa.eu/label/Example
pdf: https://eprel.ec.europa.eu/fiches/Example.pdf
energy_class: A
energy_scale: A-G
total_items: 1
processed_at: '2026-08-04T10:04:12Z'
2026-08-17 21:20:45 +02:00
ProcessInProgressExample:
summary: In-progress poll
value:
data:
status: PROCESSING
process_id: 8f14e45f-ceea-467a-9e5d-9c6b0e8f3a21
processing_type: full
created_at: '2026-08-04T09:59:50Z'
started_at: '2026-08-04T10:00:00Z'
2026-08-17 21:20:45 +02:00
ProcessFailedExample:
summary: Failed poll
value:
data:
status: FAILED
process_id: 8f14e45f-ceea-467a-9e5d-9c6b0e8f3a21
processing_type: full
error: Processing failed
responses:
Unauthorized:
2026-08-17 21:20:45 +02:00
description: Missing or invalid API key (RequireAPIKey / CodedError envelope).
content:
application/json:
schema:
2026-08-17 21:20:45 +02:00
$ref: "#/components/schemas/CodedAPIError"
example:
error:
code: unauthorized
message: Unauthorized
Forbidden:
description: Wrong company or insufficient role. Flat error string. Cross-tenant resource ids on
API-key routes usually return 404 instead.
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: forbidden
NotFound:
description: Resource not found for this API key company (missing id or wrong tenant).
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Dashboard-shared handlers
value:
error: not found
2026-08-17 21:20:45 +02:00
coded:
summary: Coded v1Err helpers
value:
error:
code: not_found
message: Not found
BadRequest:
description: Invalid request or client validation error (handlers use HTTP 400).
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error helper
value:
error: invalid json
2026-08-17 21:20:45 +02:00
coded:
summary: Coded v1Err validation
value:
error:
code: validation_error
message: '''items'' array is required'
ValidationError:
description: Validation error. Public v1 handlers return HTTP 400 for these cases (OpenAPI also
lists 422 for clients that expect that status).
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat validation
value:
error: invalid id
2026-08-17 21:20:45 +02:00
coded:
summary: Coded validation
value:
error:
code: validation_error
message: invalid id
Conflict:
description: Conflict (for example cannot demote or remove the last company admin).
content:
application/json:
schema:
$ref: "#/components/schemas/FlatAPIError"
example:
error: cannot demote the last admin
TooManyRequests:
description: "Rate limited (RateLimitV1Process heavy mutations and/or processing StartLimiter).\
\ May include Retry-After: 60."
headers:
Retry-After:
schema:
type: integer
description: Seconds until retry (set by RateLimitV1Process)
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Middleware limiter
value:
error: rate limit exceeded
2026-08-17 21:20:45 +02:00
coded:
summary: Processing StartLimiter
value:
error:
code: rate_limited
message: rate limit exceeded
InternalServerError:
description: Unexpected server failure (auth backend, enqueue, list/get failures).
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/FlatAPIError"
2026-08-17 21:20:45 +02:00
- $ref: "#/components/schemas/CodedAPIError"
examples:
flat:
summary: Flat Error helper
value:
error: list failed
2026-08-17 21:20:45 +02:00
coded:
summary: Coded internal error
value:
error:
code: internal_error
message: list failed
`)