{"openapi":"3.1.0","info":{"title":"Feedcast marketing site API","version":"1.0.0","summary":"Public and authenticated HTTP APIs on feedcast.ai","description":"HTTP APIs for the Feedcast marketing site. These endpoints cover contact, merchant-docs search, Donna support chat, authenticated product-news ingestion, and ISR revalidation. There is no public catalog-management API here — merchant product APIs live in the Feedcast app.\n\nVersioning: the current major version is v1 (`info.version` 1.0.0). Prefer `/api/v1/*` or send `X-API-Version: 1`. Unversioned `/api/*` paths are stable aliases of v1 and keep the same request and response contracts.\n\nDeprecation: breaking changes ship as `/api/v2/` (or a later major). Deprecated operations set the OpenAPI `deprecated` flag. When a sunset date is known, responses include `Deprecation` and `Sunset` headers (RFC 8594). No v1 operation is deprecated today.\n\nRate limits: successful and 429 responses include RFC RateLimit headers (`RateLimit`, `RateLimit-Policy`, `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`). 429 responses also include `Retry-After`.","contact":{"name":"Feedcast","email":"support@feedcast.ai","url":"https://feedcast.ai/en/developers"},"license":{"name":"Proprietary","url":"https://feedcast.ai/en/terms-of-service"}},"servers":[{"url":"https://feedcast.ai","description":"Production"}],"externalDocs":{"description":"Feedcast developer resources, llms.txt, and MCP","url":"https://feedcast.ai/en/developers"},"tags":[{"name":"public","description":"Unauthenticated, rate-limited marketing APIs"},{"name":"authenticated","description":"Bearer-secret operator APIs"}],"paths":{"/api/v1/contact":{"post":{"operationId":"submitContactForm","tags":["public"],"summary":"Submit the Feedcast contact form","description":"Validates and emails a contact request. Honeypot field `website` must be empty. Limit: 5 requests / 15 minutes per hashed IP. Optional Idempotency-Key header (1–256 chars `[A-Za-z0-9_./:-]`).","parameters":[{"name":"X-API-Version","in":"header","required":false,"description":"Optional API major version. Defaults to 1. Equivalent to calling the matching /api/v1/* path.","schema":{"type":"string","enum":["1"],"default":"1"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[A-Za-z0-9_./:-]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactRequest"}}}},"responses":{"200":{"description":"Message accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactSuccess"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying a 429.","schema":{"type":"integer"}}}},"500":{"description":"Contact form is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Email delivery failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Rate limiter unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/contact":{"post":{"operationId":"submitContactFormUnversioned","tags":["public"],"summary":"Submit the Feedcast contact form","description":"Validates and emails a contact request. Honeypot field `website` must be empty. Limit: 5 requests / 15 minutes per hashed IP. Optional Idempotency-Key header (1–256 chars `[A-Za-z0-9_./:-]`). Unversioned alias of `/api/v1/contact` (API v1).","parameters":[{"name":"X-API-Version","in":"header","required":false,"description":"Optional API major version. Defaults to 1. Equivalent to calling the matching /api/v1/* path.","schema":{"type":"string","enum":["1"],"default":"1"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[A-Za-z0-9_./:-]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactRequest"}}}},"responses":{"200":{"description":"Message accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactSuccess"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Request body too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying a 429.","schema":{"type":"integer"}}}},"500":{"description":"Contact form is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Email delivery failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Rate limiter unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/docs/search":{"get":{"operationId":"searchDocs","tags":["public"],"summary":"Search Feedcast merchant documentation","description":"Public excerpt-only search over published docs. Queries shorter than 2 characters return an empty result set. Database failures fail open with HTTP 200 and `results: []` so clients must not treat empty as “no matches” when the service is degraded. Limit: 30 requests / 60 seconds per hashed IP.","parameters":[{"name":"X-API-Version","in":"header","required":false,"description":"Optional API major version. Defaults to 1. Equivalent to calling the matching /api/v1/* path.","schema":{"type":"string","enum":["1"],"default":"1"}},{"name":"q","in":"query","required":true,"description":"Search query (2–120 characters).","schema":{"type":"string","minLength":2,"maxLength":120}},{"name":"locale","in":"query","required":false,"description":"Docs locale. Invalid values fall back to `en`.","schema":{"type":"string","enum":["en","fr"],"default":"en"}}],"responses":{"200":{"description":"Scored excerpt results (may be empty)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocsSearchSuccess"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Query too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying a 429.","schema":{"type":"integer"}}}},"503":{"description":"Rate limiter unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/docs/search":{"get":{"operationId":"searchDocsUnversioned","tags":["public"],"summary":"Search Feedcast merchant documentation","description":"Public excerpt-only search over published docs. Queries shorter than 2 characters return an empty result set. Database failures fail open with HTTP 200 and `results: []` so clients must not treat empty as “no matches” when the service is degraded. Limit: 30 requests / 60 seconds per hashed IP. Unversioned alias of `/api/v1/docs/search` (API v1).","parameters":[{"name":"X-API-Version","in":"header","required":false,"description":"Optional API major version. Defaults to 1. Equivalent to calling the matching /api/v1/* path.","schema":{"type":"string","enum":["1"],"default":"1"}},{"name":"q","in":"query","required":true,"description":"Search query (2–120 characters).","schema":{"type":"string","minLength":2,"maxLength":120}},{"name":"locale","in":"query","required":false,"description":"Docs locale. Invalid values fall back to `en`.","schema":{"type":"string","enum":["en","fr"],"default":"en"}}],"responses":{"200":{"description":"Scored excerpt results (may be empty)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocsSearchSuccess"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Query too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying a 429.","schema":{"type":"integer"}}}},"503":{"description":"Rate limiter unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/support/runs":{"post":{"operationId":"createSupportRun","tags":["public"],"summary":"Start a Donna support chat run","description":"Public BFF that proxies to the Donna support agent. Successful responses are `text/event-stream` (SSE), not JSON. May set an `fc_donna_vid` cookie. Limit: 30 requests / 15 minutes per hashed IP.","parameters":[{"name":"X-API-Version","in":"header","required":false,"description":"Optional API major version. Defaults to 1. Equivalent to calling the matching /api/v1/* path.","schema":{"type":"string","enum":["1"],"default":"1"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportRunRequest"}}}},"responses":{"200":{"description":"SSE event stream from Donna","content":{"text/event-stream":{"schema":{"type":"string"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying a 429.","schema":{"type":"integer"}}}},"502":{"description":"Upstream Donna error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Support chat unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/support/runs":{"post":{"operationId":"createSupportRunUnversioned","tags":["public"],"summary":"Start a Donna support chat run","description":"Public BFF that proxies to the Donna support agent. Successful responses are `text/event-stream` (SSE), not JSON. May set an `fc_donna_vid` cookie. Limit: 30 requests / 15 minutes per hashed IP. Unversioned alias of `/api/v1/support/runs` (API v1).","parameters":[{"name":"X-API-Version","in":"header","required":false,"description":"Optional API major version. Defaults to 1. Equivalent to calling the matching /api/v1/* path.","schema":{"type":"string","enum":["1"],"default":"1"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportRunRequest"}}}},"responses":{"200":{"description":"SSE event stream from Donna","content":{"text/event-stream":{"schema":{"type":"string"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"Invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}},"headers":{"RateLimit":{"description":"RFC 9331 quota snapshot, e.g. `\"docsSearch\";r=29;t=60`.","schema":{"type":"string"}},"RateLimit-Policy":{"description":"Quota policy, e.g. `30;w=60`.","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Maximum requests in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying a 429.","schema":{"type":"integer"}}}},"502":{"description":"Upstream Donna error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Support chat unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/blog/ingest":{"post":{"operationId":"ingestBlogPost","tags":["authenticated"],"summary":"Upsert a product-news post","description":"Authenticated upsert into `blog_posts` on `(slug, locale)` used by the feedbot-agno content factory. Requires `Authorization: Bearer <BLOG_INGEST_SECRET>`. Revalidates localized indexes, the post, RSS, and the sitemap.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogIngestRequest"}}}},"responses":{"200":{"description":"Post upserted and paths revalidated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogIngestSuccess"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Configuration or upsert failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/blog/ingest":{"post":{"operationId":"ingestBlogPostUnversioned","tags":["authenticated"],"summary":"Upsert a product-news post","description":"Authenticated upsert into `blog_posts` on `(slug, locale)` used by the feedbot-agno content factory. Requires `Authorization: Bearer <BLOG_INGEST_SECRET>`. Revalidates localized indexes, the post, RSS, and the sitemap. Unversioned alias of `/api/v1/blog/ingest` (API v1).","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogIngestRequest"}}}},"responses":{"200":{"description":"Post upserted and paths revalidated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogIngestSuccess"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Configuration or upsert failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/v1/revalidate":{"post":{"operationId":"revalidateMarketingPaths","tags":["authenticated"],"summary":"Revalidate blog or docs ISR paths","description":"Authenticated on-demand revalidation. Requires `Authorization: Bearer <REVALIDATE_SECRET>`. `resource` is `blog` (default) or `docs`.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevalidateRequest"}}}},"responses":{"200":{"description":"Paths revalidated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevalidateSuccess"}}}},"400":{"description":"Invalid body or resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Configuration or revalidation failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/revalidate":{"post":{"operationId":"revalidateMarketingPathsUnversioned","tags":["authenticated"],"summary":"Revalidate blog or docs ISR paths","description":"Authenticated on-demand revalidation. Requires `Authorization: Bearer <REVALIDATE_SECRET>`. `resource` is `blog` (default) or `docs`. Unversioned alias of `/api/v1/revalidate` (API v1).","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevalidateRequest"}}}},"responses":{"200":{"description":"Paths revalidated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevalidateSuccess"}}}},"400":{"description":"Invalid body or resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Configuration or revalidation failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Shared secret. Use `BLOG_INGEST_SECRET` for `/api/blog/ingest` and `REVALIDATE_SECRET` for `/api/revalidate`."}},"schemas":{"ApiError":{"type":"object","additionalProperties":false,"required":["error","code","message","hint"],"properties":{"error":{"type":"string","description":"Human-readable error (same text as message, for existing clients)."},"code":{"type":"string","enum":["UNAUTHORIZED","NOT_CONFIGURED","VALIDATION_ERROR","PAYLOAD_TOO_LARGE","RATE_LIMITED","UPSTREAM_ERROR","UNAVAILABLE","NOT_FOUND","METHOD_NOT_ALLOWED","INTERNAL_ERROR"]},"message":{"type":"string"},"hint":{"type":"string","description":"How an agent or client should recover."},"field":{"type":"string"}}},"ContactRequest":{"type":"object","additionalProperties":false,"required":["name","email","message","locale"],"properties":{"name":{"type":"string","minLength":1},"email":{"type":"string","format":"email"},"message":{"type":"string","minLength":1},"locale":{"type":"string","enum":["en","fr"]},"company":{"type":"string"},"website":{"type":"string","description":"Honeypot. Must be empty for legitimate submissions."}}},"ContactSuccess":{"type":"object","additionalProperties":false,"required":["success"],"properties":{"success":{"type":"boolean","const":true},"id":{"type":"string"}}},"DocsSearchSuccess":{"type":"object","additionalProperties":false,"required":["results"],"properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/DocsSearchResult"}}}},"DocsSearchResult":{"type":"object","additionalProperties":false,"required":["slug","title","category","excerpt"],"properties":{"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"category":{"type":"string"},"excerpt":{"type":"string"},"score":{"type":"number"}}},"SupportRunRequest":{"type":"object","additionalProperties":false,"required":["message"],"properties":{"message":{"type":"string","minLength":1,"maxLength":4000},"language":{"type":"string","enum":["en","fr","auto"]},"page_path":{"type":"string","pattern":"^/"}}},"BlogIngestRequest":{"type":"object","additionalProperties":false,"required":["slug","locale","title","description","content"],"properties":{"slug":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"locale":{"type":"string","enum":["en","fr"]},"title":{"type":"string","maxLength":200},"description":{"type":"string","maxLength":500},"content":{"type":"string","description":"Markdown body"},"category":{"type":"string"},"tags":{"type":"array","items":{"type":"string"},"maxItems":20},"featured_image":{"type":"string","format":"uri"},"date":{"type":"string","format":"date-time"},"draft":{"type":"boolean"},"paired_slug":{"type":"string"}}},"BlogIngestSuccess":{"type":"object","additionalProperties":false,"required":["success","revalidated"],"properties":{"success":{"type":"boolean"},"post":{"type":"object","additionalProperties":true},"revalidated":{"type":"boolean"}}},"RevalidateRequest":{"type":"object","additionalProperties":false,"properties":{"resource":{"type":"string","enum":["blog","docs"]},"type":{"type":"string","enum":["index","post","page","all"]},"slug":{"type":"string"},"locale":{"type":"string","enum":["en","fr"]}}},"RevalidateSuccess":{"type":"object","additionalProperties":false,"required":["success","revalidated","now","resource"],"properties":{"success":{"type":"boolean"},"revalidated":{"type":"boolean"},"now":{"type":"number"},"resource":{"type":"string","enum":["blog","docs"]},"type":{"type":"string"},"slug":{"type":"string"},"locale":{"type":"string"}}}}}}