# Keystone Developers Reference for every endpoint in the Keystone platform's API contracts and for the Keystone MCP server. This file is the complete developer reference as one Markdown document, generated from the platform's OpenAPI contracts and the MCP server's tool registry. Canonical HTML: https://developers.keystone.app. Generated 2026-10-10. ## Contents - Guides - Public API (42 endpoints) - Edits API (14 endpoints) - Console API (434 endpoints) - Agent API (46 endpoints) - Internal API (157 endpoints) - Webhooks and callbacks (7 endpoints) - System (4 endpoints) - Auth service (45 endpoints) - Keystone MCP server ## Hosts - SOR (business data): https://sor.keystone.app - Auth (Heimdal): https://auth.keystone.app - MCP: https://mcp.keystone.app/mcp # Guides ## Conventions What every Keystone endpoint has in common: one envelope, one error shape, UUID ids, and a surface per audience. HTML: https://developers.keystone.app/guides/conventions/overview/ Keystone's HTTP API is one FastAPI service (the Statement of Record, "SOR") plus a separate auth service (Heimdal). The two publish OpenAPI 3.1 documents, and this site is generated from them. A few conventions hold across every endpoint; the pages in this group describe them so the [reference](https://developers.keystone.app/api/) can stay terse. ### In one paragraph Requests are JSON over HTTPS. Every successful response is wrapped in a `SuccessResponse` envelope with a `request_id`, `success: true`, and the payload under `data`; every error is an `ErrorResponse` with the same `request_id`, `success: false`, and an `error` object with a `code` and `message`. Identifiers are UUIDs. Paths are versioned at `/api/v1/`. Endpoints are organized into **surfaces** by who calls them and what credential they carry; see [Surfaces](https://developers.keystone.app/guides/conventions/surfaces/). ### Where to look | Question | Page | | --- | --- | | Which endpoints can my site or app call? | [Public API](https://developers.keystone.app/api/public/) and [Edits API](https://developers.keystone.app/api/edits/) | | What does the console use? | [Console API](https://developers.keystone.app/api/console/) | | How does an assistant edit a business? | [MCP server](https://developers.keystone.app/mcp/) and [How a session works](https://developers.keystone.app/guides/mcp/sessions/) | | What does a response look like? | [Envelopes](https://developers.keystone.app/guides/conventions/envelopes/) | | What does a failure look like? | [Errors](https://developers.keystone.app/guides/conventions/errors/) | > **Scope of this site** > This is a reference: what exists and what it takes and returns. Onboarding, credentials, and account setup are handled with Keystone directly and are not documented here. ## Surfaces The eight groups of endpoints, by audience and credential, and how they map onto the two services. HTML: https://developers.keystone.app/guides/conventions/surfaces/ The raw OpenAPI documents list several hundred paths with little structure beyond tags. This site assigns every operation to a **surface** by its path prefix. A surface has one audience and one credential, so once you know which surface you are on you know what every request carries. ### SOR surfaces | Surface | Prefix | Audience | Credential | | --- | --- | --- | --- | | [Public API](https://developers.keystone.app/api/public/) | `/api/v1/public` | A business's website or headless front end | `X-API-Key` (the site's key) | | [Edits API](https://developers.keystone.app/api/edits/) | `/api/v1/edits` | Assistants and tools making reviewed changes | Console bearer token, or MCP bearer token with `X-Internal-Api-Key` | | [Console API](https://developers.keystone.app/api/console/) | `/api/v1/admin`, `/api/v1/businesses`, `/api/v1/ai` | The Keystone console | Console bearer token | | [Agent API](https://developers.keystone.app/api/agent/) | `/api/v1/agent` | Keystone's own agents | `X-Internal-Api-Key` | | [Internal API](https://developers.keystone.app/api/internal/) | `/api/v1/internal` | Other Keystone services | `X-Internal-Api-Key` | | [Webhooks and callbacks](https://developers.keystone.app/api/webhooks/) | `/api/v1/webhooks`, `/api/v1/ads/webhook`, `/api/v1/voice` | GitHub, Entri, Meta, the voice provider | The provider's signature | | [System](https://developers.keystone.app/api/system/) | `/health`, `/api/health`, `/api/v1/sites` | Probes and the sites registry | None | ### Heimdal | Surface | Prefix | Audience | Credential | | --- | --- | --- | --- | | [Auth service](https://developers.keystone.app/api/auth/) | `/api/v1/auth`, `/users`, `/roles`, `/permissions`, `/oauth`, `/consumer`, `/internal` | Console, websites, MCP clients, services | Credentials on sign-in; bearer tokens after; `X-Internal-Api-Key` for `internal` | ### Groups Inside a surface, operations are grouped by the resource they manage: the spec's resource tag where FastAPI emitted one (`contacts`, `website_mod`, `ads_campaigns`), otherwise the first path segment after the prefix. Group pages carry every operation in full; surface pages list them one per line. ### Why the console surface is so large The Console API holds everything `go.keystone.app` does, for every area of the product. Most of it is per-business and scoped by the caller's memberships. Platform-staff endpoints (accounts, platform admins, promos, pricing) sit alongside and refuse non-staff callers. > **If you are building on Keystone** > You almost certainly want the Public API for reading and the Edits API (directly or through MCP) for writing. The rest is here so the whole contract is visible, and so internal teams share one reference. ## Envelopes The SuccessResponse wrapper every 2xx carries, and the metadata it can include. HTML: https://developers.keystone.app/guides/conventions/envelopes/ Every successful JSON response from SOR is wrapped: ```json { "request_id": "7f9d1c2e-...", "success": true, "data": { ... }, "metadata": null, "meta": null } ``` | Field | Type | Notes | | --- | --- | --- | | `request_id` | string | Unique per request. Quote it when reporting a problem. | | `success` | `true` | Always true on this envelope. Errors use a different shape; see [Errors](https://developers.keystone.app/guides/conventions/errors/). | | `data` | object or array | The payload. Its schema is the `SuccessResponse__` type on the endpoint, where `` is the inner model. | | `metadata` | `ResponseMetadata` or null | Timing and pagination where an endpoint pages. | | `meta` | `ResponseMeta` or null | Endpoint-specific extras, such as the business's time zone or a freshness marker. | ### Reading the reference On an endpoint's page the 200 response is labelled with the envelope type, for example `SuccessResponse_PublicService_`. Expand it to see `data` and its fields. The inner model is what you will work with; the envelope is the same everywhere. ### Lists List endpoints return `data` as an array. Where a list pages, `metadata` carries the page and total; the query parameters that drive it are on the endpoint (`page`, `limit`, `offset`, or a cursor, depending on the resource). ### Non-JSON responses A few endpoints return a file (a sitemap, an `llms.txt`, a media download) or a server-sent event stream. They are marked by their content type in the reference and carry no envelope. ### Heimdal The auth service returns plain JSON bodies without the SOR envelope. Each endpoint's response schema is shown as-is. ## Errors The ErrorResponse shape, the status codes you will see, and what the codes mean. HTML: https://developers.keystone.app/guides/conventions/errors/ Every error from SOR has one shape: ```json { "request_id": "7f9d1c2e-...", "success": false, "error": { "code": "not_found", "message": "Service not found", "details": null } } ``` | Field | Notes | | --- | --- | | `request_id` | The same id a success would carry. | | `error.code` | A stable, machine-readable string. | | `error.message` | Human-readable. May change; do not match on it. | | `error.details` | Optional structured context: validation errors by field, or, on the Edits API, the refused items by index. | ### Status codes | Status | When | | --- | --- | | `400` | The request is malformed in a way validation could not express. | | `401` | Missing or invalid credential for the surface. | | `403` | Valid credential, but not allowed: wrong business, wrong role, or a policy that refuses. | | `404` | No such resource, or a feature that is off for this business. | | `409` | Conflict: compare-and-swap failed, a claim is held by someone else, a slug is taken. | | `422` | Validation error. `details` lists the fields. | | `429` | Rate limited. Retry after the interval in `Retry-After`. | | `500` | Server error. Quote the `request_id`. | Every endpoint in the reference lists the codes it can return, all with the `ErrorResponse` body. ### On the Edits API A staged changeset can be partly refused: items that fail validation or compare-and-swap are listed in `error.details.items` with their `index`, `entity`, `code`, and `message`, so a caller can fix only those. The [MCP server](https://developers.keystone.app/mcp/) passes this through to the model verbatim. ## Identifiers and versions UUIDs everywhere, slugs for public URLs, and record versions for safe writes. HTML: https://developers.keystone.app/guides/conventions/identifiers/ ### Ids Every record has a UUID `id`. Businesses, locations, services, service items, packages, offers, team members, FAQs, job postings, contacts, conversations, posts, campaigns: all UUIDs, all stable for the life of the record. Path parameters named `business_id`, `service_id`, and so on take these. The reference shows them as `string (uuid)`. ### Slugs Public, URL-facing records (services, packages, blog posts) also carry a `slug`. The Public API offers `by_slug` lookups so a site can resolve a URL without knowing the id. Slugs are unique within a business and may be changed by the business; treat the id as the identity and the slug as a label. ### Versions Records that can be edited through the [Edits API](https://developers.keystone.app/api/edits/) carry a `version` string. Send it back, or the `expected` values you read, when proposing a change; a write against a stale version is refused with `409` rather than silently overwriting. The [MCP server](https://developers.keystone.app/mcp/) does this for you when the model includes `expected` or `version` on a change item. ### Time Timestamps are ISO 8601 in UTC. Hours of operation and scheduling use the business's `scheduling_timezone` from its profile. ## Connecting an MCP client The Keystone MCP server speaks standard Streamable HTTP and OAuth 2.1. Anything that can connect to an MCP server can connect to it. HTML: https://developers.keystone.app/guides/mcp/connect/ The server is not built for one assistant. It implements the Model Context Protocol over Streamable HTTP, authenticates with OAuth 2.1 the way the protocol specifies, and publishes the discovery documents a client needs to find the authorization server on its own. Claude, Cursor, ChatGPT, the MCP Inspector, and anything you write against an MCP SDK connect the same way. The [Keystone Claude Connector](https://developers.keystone.app/guides/mcp/claude/) page covers the one client with a packaged setup. ### What a client needs | | | | --- | --- | | Server URL | `https://mcp.keystone.app/mcp` | | Transport | Streamable HTTP (the 2026-07-28 protocol; 2025-era clients are served too) | | Authentication | OAuth 2.1 with PKCE; bearer token in the `Authorization` header | | Scope | `edits` | | Discovery | `/.well-known/oauth-protected-resource` (RFC 9728) on the server's host; the authorization server's metadata is mirrored at `/.well-known/oauth-authorization-server` for older clients | ### The sign-in 1. The client calls the server without a token and receives `401` with a `WWW-Authenticate` header pointing at the protected-resource metadata. 2. From that document it learns the authorization server (the Keystone [Auth service](https://developers.keystone.app/api/auth/)) and the `edits` scope, and starts an authorization-code flow with PKCE. 3. The user signs in to Keystone and approves the request. The Auth service issues an MCP access token bound to this server as its audience. 4. The client sends the token on every call. The server verifies the signature, issuer, audience, and token type, then forwards the call to the [Edits API](https://developers.keystone.app/api/edits/) with that token and its own service key. ### Who may connect Two things are registered on the Keystone side, not by the client: - **The redirect URI.** The Auth service only redirects to URIs it knows. Claude's and Cursor's are registered; for another client, or your own, send its callback URI to developer support. - **The user.** MCP access is granted per Keystone user. A sign-in from a user who has not been granted it fails at the Auth service with a reason. Everything after sign-in is governed by SOR, exactly as in the console: who may edit which business, the claim, the launch state, and compare-and-swap on every field. ### Cards The server ships an MCP App (the card the user reviews changes on). A client that renders MCP Apps shows it; a client that does not still gets every tool's text result and `structuredContent`, and the four card-only tools stay out of the model's reach either way. [Cards and card tokens](https://developers.keystone.app/guides/mcp/cards/) explains why. > **Trying it without an assistant** > The MCP Inspector connects from the browser: paste the server URL, choose Streamable HTTP, and complete the OAuth flow in the popup. The server allows cross-origin calls to `/mcp` for exactly this. ## Keystone Claude Connector Adding the Keystone MCP server to Claude as a custom connector, for an organization or for one account. HTML: https://developers.keystone.app/guides/mcp/claude/ Claude connects to the Keystone MCP server as a **custom connector**. Nothing about the server is specific to Claude ([Connecting an MCP client](https://developers.keystone.app/guides/mcp/connect/) is the general case); this page is the Claude-side setup. ### For an organization 1. An organization owner opens **Organization settings → Connectors** and chooses **Add custom connector**. 2. Name it **Keystone** and enter the URL `https://mcp.keystone.app/mcp`. Leave the OAuth client fields empty; Claude discovers the authorization server from the URL. 3. Save. The connector appears for members; each member connects it from their own settings and signs in to Keystone once. ### For one account In **Settings → Connectors**, add a custom connector with the same URL, then **Connect** and sign in to Keystone. ### Using it In a conversation, enable the Keystone connector and ask for a business by name, website, or domain. Claude calls `find_business`; the picker card lists the matches. Click one to open a session. From there Claude reads the business and proposes changes; you review them on the card and apply with a click. [How a session works](https://developers.keystone.app/guides/mcp/sessions/) walks the whole sequence, and the [tool reference](https://developers.keystone.app/mcp/) lists what Claude can and cannot call. > **If the connection fails** > The sign-in finishes at Keystone, not at Claude. A refusal there means the user has not been granted MCP access yet; ask developer support. Claude's callback URI is already registered with the Auth service, so a redirect error is not something to fix on the Claude side. ## How a session works Find, open, snapshot, propose, apply: the life of an edit through the MCP server. HTML: https://developers.keystone.app/guides/mcp/sessions/ The Keystone MCP server lets an assistant change a business's information without ever being able to change it alone. Every write passes through a human click on a card. This page walks the sequence; the [tool reference](https://developers.keystone.app/mcp/) has each tool's schema. ### The sequence 1. **Find.** The model calls `find_business` with a name, website, or domain. The server lists matches and shows a picker card. Each match is **editable** (the user holds a claim on it and its site is not live) or **locked** with a reason. 2. **Open.** The user clicks a business on the card. The card calls `open_session` with a card token minted for that exact click; the server opens an edit session on the Edits API and returns the `session_id` to the model. The model cannot do this step. 3. **Read.** The model calls `get_snapshot` with the session id: the profile and every record with its id, label, and `version`. `read_business` returns one record in full with its write field names. 4. **Propose.** The model calls `propose_changes` with up to 50 change items, each carrying the `expected` values or `version` it read. The server stages a changeset; nothing is saved. The card shows each change, ticked, naming the business. 5. **Apply.** The user unticks what they do not want and clicks Apply. The card calls `apply_changeset` with its token; SOR applies each item with compare-and-swap and writes it to Change History. 6. **Undo, if needed.** The card offers Undo; `undo_changeset` reverts the applied items. A value changed again since the apply is kept. ### Claims A business can be edited only while the user **holds a claim** on it (24 hours, extendable) and its website is not live. A business nobody holds, and that has not launched, shows **Claim** on the picker; `claim_business` takes it. Live businesses are edited in the console, where Change History and Undo work the same way. ### Sessions expire A session has an expiry returned by `open_session`. After it, `get_snapshot` and `propose_changes` fail with a reason; find and open again. ### Refusals Anything SOR refuses (not claimed, launched, conflict, validation) comes back as the tool's text result with the code, and for a changeset, each refused item by position. The model is expected to fix and resend only those. > **Idempotency** > `propose_changes` derives an idempotency key from the session and the items, so a retried call stages the same changeset once. ## Cards and card tokens Why some tools can only be called by the card, and how card tokens bind a click to an action. HTML: https://developers.keystone.app/guides/mcp/cards/ The server ships an MCP App: a card the client renders beside the chat. The card is where the human acts. Four tools exist only for it: `claim_business`, `open_session`, `apply_changeset`, `undo_changeset`. ### Visibility These tools are registered with `visibility: ["app"]`, so a conforming client does not offer them to the model. Even if a model called one, it could not succeed: each requires a **card token**. ### Card tokens When a tool returns a card, it also returns `_meta.cardTokens`: one token per click the card may make next, each bound to the signed-in user, one action, and one target. | Returned by | Token | Lets the card call | | --- | --- | --- | | `find_business` | `open:` for each editable match; `claim:` for each claimable one | `open_session`, `claim_business` | | `claim_business` | `open:` | `open_session` | | `propose_changes` | `apply` | `apply_changeset` | | `apply_changeset` | `undo` | `undo_changeset` | The server verifies the token against the caller, the action, and the target before forwarding to the Edits API. A token for applying changeset A cannot apply changeset B; a token minted for one user is refused for another. ### What the card shows | View | After | | --- | --- | | `picker` | `find_business`: the matches, editable or locked, with Open or Claim. | | `snapshot` | `get_snapshot`: what needs work, with a link to the console. | | `changeset` | `propose_changes`, `apply_changeset`, `undo_changeset`: each item with its state and a tick box. | | `session` | `open_session`: the business and the session's expiry. | | `claimed` | `claim_business`: the claim's expiry. | `structuredContent.view` names the view; the card reads the rest of `structuredContent` for its data. ### Without a card A client with no MCP Apps support still gets the text results. `propose_changes` includes a console link where the user can apply the staged changeset in Keystone instead. # Public API Read a business's published data: profile, services, packages, team, FAQ, posts, forms. The surface a business's website runs on. Every Keystone site, and any headless front end a business builds, reads its content from these endpoints with the site's API key. Responses are scoped to the one business the key belongs to. The `@keystone-sites/core` package wraps this surface. - Audience: For businesses and partners - Base URL: https://sor.keystone.app - Authentication: Site API key, sent as the `X-API-Key` header. - Endpoints: 42 in 22 groups - HTML: https://developers.keystone.app/api/public/ ## Public API: Ads config 1 endpoints. HTML: https://developers.keystone.app/api/public/ads-config/ ### GET /api/v1/public/ads_config Get ads config (Meta pixel id) for the calling site Operation id: `get_ads_config_api_v1_public_ads_config_get` Browser ad-tracking config for the generated site. Returns the business' Meta pixel id (``{"meta_pixel_id": ...}``) so the site can init the Pixel. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Analytics config 1 endpoints. HTML: https://developers.keystone.app/api/public/analytics-config/ ### GET /api/v1/public/analytics_config Get per-site analytics config (PostHog, GTM, environment) Operation id: `get_analytics_config_api_v1_public_analytics_config_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Blog posts 4 endpoints. HTML: https://developers.keystone.app/api/public/blog-posts/ ### GET /api/v1/public/blog_posts List published blog posts (public) Operation id: `list_blog_posts_api_v1_public_blog_posts_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_dict_str__Any___ - `request_id` · string · required - `success` · true - `data` · object[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/blog_posts/by_slug/{slug} Get a published blog post by slug (public) Operation id: `get_blog_post_by_slug_api_v1_public_blog_posts_by_slug__slug__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `slug` | path | string | yes | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/blog_posts/featured List featured blog posts (public) Operation id: `list_blog_posts_featured_api_v1_public_blog_posts_featured_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_dict_str__Any___ - `request_id` · string · required - `success` · true - `data` · object[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/blog_posts/recent List most recent published blog posts (public) Operation id: `list_blog_posts_recent_api_v1_public_blog_posts_recent_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_dict_str__Any___ - `request_id` · string · required - `success` · true - `data` · object[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Chat 3 endpoints. HTML: https://developers.keystone.app/api/public/chat/ ### GET /api/v1/public/chat/config Webchat config for the calling site (gates the widget) Operation id: `get_chat_config_api_v1_public_chat_config_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/chat/messages Fetch webchat history (long-poll for new messages) Operation id: `get_chat_messages_api_v1_public_chat_messages_get` Return messages with ``id > since`` (whole thread when ``since`` omitted). Holds up to ``wait`` seconds (capped) until new messages arrive — the AI reply lands async, so the held request returns as soon as it's persisted. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `session_id` | query | string | yes | | | `since` | query | string \| null | no | | | `wait` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/public/chat/messages Send a webchat message (anonymous visitor) Operation id: `send_chat_message_api_v1_public_chat_messages_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Request body** (application/json, required): ChatMessageBody - `session_id` · string · required - `body` · string · required - `display_name` · string | null - `email` · string | null - `phone` · string | null - `first_name` · string | null - `last_name` · string | null - `fbp` · string | null - `fbc` · string | null - `page_url` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Company information 1 endpoints. HTML: https://developers.keystone.app/api/public/company-information/ ### GET /api/v1/public/company_information Get company information Operation id: `get_company_information_api_v1_public_company_information_get` Company information for the authenticated business. 404 if business not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PublicCompanyInformation_ - `request_id` · string · required - `success` · true - `data` · PublicCompanyInformation · required: Company information for GET /company_information. Matches legacy CompanyInformation. - `id` · string · required - `company_name` · string | null - `tagline` · string | null - `mission_statement_markdown` · string | null - `about_text_markdown` · string | null - `about_markdown` · string | null - `description_markdown` · string | null - `values_markdown` · string | null - `values` · PublicCompanyValue[] - `stats_markdown` · string | null - `founded_year` · integer | null - `business_hours` · PublicBusinessHours | object | null - `external_management_url` · string | null - `portal_url` · string | null - `primary_phone` · string | null - `primary_email` · string | null - `address_line_1` · string | null - `address_line_2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `support_email` · string | null - `sales_email` · string | null - `facebook_url` · string | null - `instagram_url` · string | null - `tiktok_url` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `youtube_url` · string | null - `pinterest_url` · string | null - `google_my_business_url` · string | null - `yelp_url` · string | null - `tripadvisor_url` · string | null - `google_reviews_url` · string | null - `logo_photo` · PublicPhoto | null - `favicon_url` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `account_status` · string | null - `chat_enabled` · boolean - `terms_of_service_markdown` · string | null - `privacy_policy_markdown` · string | null - `managed_phone_numbers` · any[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: FAQ questions 2 endpoints. HTML: https://developers.keystone.app/api/public/faq-questions/ ### GET /api/v1/public/faq_questions List FAQ questions Operation id: `list_faq_questions_api_v1_public_faq_questions_get` All FAQ questions for the business, ordered by display_order. Paginated. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicFaqQuestion__ - `request_id` · string · required - `success` · true - `data` · PublicFaqQuestion[] · required - `id` · string · required - `question` · string | null - `answer` · string | null - `answer_markdown` · string | null - `featured` · boolean - `category_slug` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/faq_questions/featured List featured FAQ questions Operation id: `list_faq_questions_featured_api_v1_public_faq_questions_featured_get` FAQ questions where is_featured is True. Paginated. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicFaqQuestion__ - `request_id` · string · required - `success` · true - `data` · PublicFaqQuestion[] · required - `id` · string · required - `question` · string | null - `answer` · string | null - `answer_markdown` · string | null - `featured` · boolean - `category_slug` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Form submissions 1 endpoints. HTML: https://developers.keystone.app/api/public/form-submissions/ ### POST /api/v1/public/form_submissions Submit a form Operation id: `submit_form_api_v1_public_form_submissions_post` Submit a form with field values. Request body must include `formType` (not form id); the API resolves the form definition for this account and stores the matching `form_id` server-side. Auth via X-API-Key header. Returns submission confirmation with optional event_id for ad tracking deduplication. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Request body** (application/json, required): object **Responses** - `200` Successful Response: SuccessResponse_PublicFormSubmissionResponse_ - `request_id` · string · required - `success` · true - `data` · PublicFormSubmissionResponse · required: Response for successful form submission. - `submission_id` · string · required - `form_type` · string · required - `message` · string - `event_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Forms 1 endpoints. HTML: https://developers.keystone.app/api/public/forms/ ### GET /api/v1/public/forms/{form_type} Get form by type Operation id: `get_form_by_type_api_v1_public_forms__form_type__get` Get a live form definition by identity (built-in or custom) for the business. Returns the form's fields and settings needed to render it client-side. Only ``live`` forms are served; an unknown identity or a non-live form returns a typed 404 (``FORM_NOT_AVAILABLE``), never a 500. Auth via X-API-Key header. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `form_type` | path | string | yes | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PublicForm_ - `request_id` · string · required - `success` · true - `data` · PublicForm · required: Form for public GET /forms/{form_type}. Matches legacy FormSerializer public view. - `id` · string · required - `name` · string · required - `form_type` · string · required - `fields` · (FormFieldSchema | FormFieldSchema[])[] - `settings` · FormSettingsSchema: Settings for form behavior and consent flags. - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Industry media 1 endpoints. HTML: https://developers.keystone.app/api/public/industry-media/ ### GET /api/v1/public/industry_media Get curated industry fill-in imagery Operation id: `get_industry_media_api_v1_public_industry_media_get` Curated industry gallery photos for the business's industries (primary first), best-first, same shape as /media_library with source="industry". Fill-in imagery for creative/atmospheric use only — never for positions presented as the business's own work (gallery, team, results). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `limit` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PublicMediaLibrary_ - `request_id` · string · required - `success` · true - `data` · PublicMediaLibrary · required: GET /media_library and /industry_media — same shape for both photo pools (one photo shape for consumers): quality photos best-first. ``total`` is the pool-grade count, so ``total > len(photos)`` signals more good photos exist than the ``limit`` returned (default 60). - `photos` · PublicMediaLibraryPhoto[] - `total` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Job postings 3 endpoints. HTML: https://developers.keystone.app/api/public/job-postings/ ### GET /api/v1/public/job_postings List job postings (placeholder) Operation id: `list_job_postings_api_v1_public_job_postings_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_dict_str__Any___ - `request_id` · string · required - `success` · true - `data` · object[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/job_postings/by_slug/{slug} Get job posting by slug (placeholder) Operation id: `get_job_posting_by_slug_api_v1_public_job_postings_by_slug__slug__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `slug` | path | string | yes | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/job_postings/featured List featured job postings (placeholder) Operation id: `list_job_postings_featured_api_v1_public_job_postings_featured_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_dict_str__Any___ - `request_id` · string · required - `success` · true - `data` · object[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: LLMs 1 endpoints. HTML: https://developers.keystone.app/api/public/llms/ ### GET /api/v1/public/llms/current Serve the website llms.txt (Markdown) Operation id: `get_llms_current_api_v1_public_llms_current_get` Serve the latest persisted llms.txt. Cold start (no version yet): build + persist once, then serve. A version cut before the column existed has ``llms_txt`` NULL: serve a fresh render for it (no persistence) and schedule the debounced refresh that will store it. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Locations 4 endpoints. HTML: https://developers.keystone.app/api/public/locations/ ### GET /api/v1/public/locations List locations Operation id: `list_locations_api_v1_public_locations_get` All active locations for the business, ordered by display_order. Paginated. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicLocation__ - `request_id` · string · required - `success` · true - `data` · PublicLocation[] · required - `id` · string · required - `name` · string | null - `slug` · string | null - `address_line_1` · string | null - `address_line_2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `country` · string | null - `is_primary` · boolean - `active` · boolean - `phone` · string | null - `email` · string | null - `description` · string | null - `description_markdown` · string | null - `timezone` · string | null - `business_hours` · PublicBusinessHours | object | null - `photo_attachments` · PublicPhotoAttachment[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/locations/{location_id} Get location by ID Operation id: `get_location_by_id_api_v1_public_locations__location_id__get` Get a single location by id. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `location_id` | path | string (uuid) | yes | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PublicLocation_ - `request_id` · string · required - `success` · true - `data` · PublicLocation · required: Location for GET /locations and by id/slug. Matches legacy Location. - `id` · string · required - `name` · string | null - `slug` · string | null - `address_line_1` · string | null - `address_line_2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `country` · string | null - `is_primary` · boolean - `active` · boolean - `phone` · string | null - `email` · string | null - `description` · string | null - `description_markdown` · string | null - `timezone` · string | null - `business_hours` · PublicBusinessHours | object | null - `photo_attachments` · PublicPhotoAttachment[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/locations/by_slug/{slug} Get location by slug Operation id: `get_location_by_slug_api_v1_public_locations_by_slug__slug__get` Get a single location by slug. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `slug` | path | string | yes | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PublicLocation_ - `request_id` · string · required - `success` · true - `data` · PublicLocation · required: Location for GET /locations and by id/slug. Matches legacy Location. - `id` · string · required - `name` · string | null - `slug` · string | null - `address_line_1` · string | null - `address_line_2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `country` · string | null - `is_primary` · boolean - `active` · boolean - `phone` · string | null - `email` · string | null - `description` · string | null - `description_markdown` · string | null - `timezone` · string | null - `business_hours` · PublicBusinessHours | object | null - `photo_attachments` · PublicPhotoAttachment[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/locations/primary Get primary location (real or placeholder) Operation id: `get_primary_location_api_v1_public_locations_primary_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Media library 1 endpoints. HTML: https://developers.keystone.app/api/public/media-library/ ### GET /api/v1/public/media_library Get the business media library (good photos, best-first) Operation id: `get_media_library_api_v1_public_media_library_get` The business's best good photos with CDN URLs, dimensions, alt, hero-grade flag, and the website_photos slot each already fills (if any). Quality-filtered, best-first, capped at ``limit`` (default 60) — a creative palette for the whole site; consumers curate further per context (e.g. the gallery renders fewer). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `limit` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PublicMediaLibrary_ - `request_id` · string · required - `success` · true - `data` · PublicMediaLibrary · required: GET /media_library and /industry_media — same shape for both photo pools (one photo shape for consumers): quality photos best-first. ``total`` is the pool-grade count, so ``total > len(photos)`` signals more good photos exist than the ``limit`` returned (default 60). - `photos` · PublicMediaLibraryPhoto[] - `total` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Messages 2 endpoints. HTML: https://developers.keystone.app/api/public/messages/ ### GET /api/v1/public/messages List messages (placeholder) Operation id: `list_messages_api_v1_public_messages_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_dict_str__Any___ - `request_id` · string · required - `success` · true - `data` · object[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/public/messages Send message (placeholder) Operation id: `send_message_api_v1_public_messages_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Request body** (application/json, required): object **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Packages 3 endpoints. HTML: https://developers.keystone.app/api/public/packages/ ### GET /api/v1/public/packages List packages Operation id: `list_packages_api_v1_public_packages_get` All packages for the business, ordered by display_order. Paginated. Member services not included. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicPackage__ - `request_id` · string · required - `success` · true - `data` · PublicPackage[] · required - `id` · string · required - `name` · string | null - `slug` · string | null - `description_markdown` · string | null - `summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `package_items` · PublicPackageItem[] - `offers` · PublicOffer[] - `featured` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/packages/by_slug/{slug} Get package by slug Operation id: `get_package_by_slug_api_v1_public_packages_by_slug__slug__get` Get a single package by slug, with member services nested. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `slug` | path | string | yes | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PublicPackage_ - `request_id` · string · required - `success` · true - `data` · PublicPackage · required: Package for GET /packages and by_slug. `cost` serializes as a decimal string (e.g. "99.99"). - `id` · string · required - `name` · string | null - `slug` · string | null - `description_markdown` · string | null - `summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `package_items` · PublicPackageItem[] - `offers` · PublicOffer[] - `featured` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/packages/featured List featured packages Operation id: `list_packages_featured_api_v1_public_packages_featured_get` Packages where is_featured is True. Paginated. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicPackage__ - `request_id` · string · required - `success` · true - `data` · PublicPackage[] · required - `id` · string · required - `name` · string | null - `slug` · string | null - `description_markdown` · string | null - `summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `package_items` · PublicPackageItem[] - `offers` · PublicOffer[] - `featured` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Reviews 2 endpoints. HTML: https://developers.keystone.app/api/public/reviews/ ### GET /api/v1/public/reviews List reviews Operation id: `list_reviews_api_v1_public_reviews_get` List public reviews for the business (legacy-compatible, non-paginated). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicReview__ - `request_id` · string · required - `success` · true - `data` · PublicReview[] · required - `id` · string · required - `reviewer_name` · string · required - `reviewer_title` · string | null - `company` · string | null - `rating` · integer · required - `content_markdown` · string | null - `source` · string | null - `source_url` · string | null - `featured` · boolean - `reviewed_at` · string | null - `created_at` · string | null - `updated_at` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/reviews/featured List featured reviews Operation id: `list_featured_reviews_api_v1_public_reviews_featured_get` List featured public reviews for the business (legacy-compatible, non-paginated). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicReview__ - `request_id` · string · required - `success` · true - `data` · PublicReview[] · required - `id` · string · required - `reviewer_name` · string · required - `reviewer_title` · string | null - `company` · string | null - `rating` · integer · required - `content_markdown` · string | null - `source` · string | null - `source_url` · string | null - `featured` · boolean - `reviewed_at` · string | null - `created_at` · string | null - `updated_at` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Service items 3 endpoints. HTML: https://developers.keystone.app/api/public/service-items/ ### GET /api/v1/public/service-items List service items Operation id: `list_service_items_api_v1_public_service_items_get` All service items for the business, ordered by display_order. Paginated. Member services not included. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicServiceItem__ - `request_id` · string · required - `success` · true - `data` · PublicServiceItem[] · required - `id` · string · required - `name` · string | null - `slug` · string | null - `description_markdown` · string | null - `summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `services` · PublicService[] - `offers` · PublicOffer[] - `featured` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/service-items/by_slug/{slug} Get service item by slug Operation id: `get_service_item_by_slug_api_v1_public_service_items_by_slug__slug__get` Get a single service item by slug, with member services nested. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `slug` | path | string | yes | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PublicServiceItem_ - `request_id` · string · required - `success` · true - `data` · PublicServiceItem · required: Service item for GET /service-items and by_slug. `cost` serializes as a decimal string (e.g. "19.99"). - `id` · string · required - `name` · string | null - `slug` · string | null - `description_markdown` · string | null - `summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `services` · PublicService[] - `offers` · PublicOffer[] - `featured` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/service-items/featured List featured service items Operation id: `list_service_items_featured_api_v1_public_service_items_featured_get` Service items where is_featured is True. Paginated. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicServiceItem__ - `request_id` · string · required - `success` · true - `data` · PublicServiceItem[] · required - `id` · string · required - `name` · string | null - `slug` · string | null - `description_markdown` · string | null - `summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `services` · PublicService[] - `offers` · PublicOffer[] - `featured` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Services 3 endpoints. HTML: https://developers.keystone.app/api/public/services/ ### GET /api/v1/public/services List services Operation id: `list_services_api_v1_public_services_get` All services for the business, ordered by display_order. Paginated. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicService__ - `request_id` · string · required - `success` · true - `data` · PublicService[] · required - `id` · string · required - `name` · string | null - `slug` · string | null - `description_markdown` · string | null - `summary` · string | null - `pricing_info` · string | null - `features` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `service_items` · PublicServiceItem[] - `featured` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/services/by_slug/{slug} Get service by slug Operation id: `get_service_by_slug_api_v1_public_services_by_slug__slug__get` Get a single service by slug. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `slug` | path | string | yes | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PublicService_ - `request_id` · string · required - `success` · true - `data` · PublicService · required: Service for GET /services and by_slug. Matches legacy Service. - `id` · string · required - `name` · string | null - `slug` · string | null - `description_markdown` · string | null - `summary` · string | null - `pricing_info` · string | null - `features` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `service_items` · PublicServiceItem[] - `featured` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/services/featured List featured services Operation id: `list_services_featured_api_v1_public_services_featured_get` Services where is_featured is True. Paginated. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicService__ - `request_id` · string · required - `success` · true - `data` · PublicService[] · required - `id` · string · required - `name` · string | null - `slug` · string | null - `description_markdown` · string | null - `summary` · string | null - `pricing_info` · string | null - `features` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `service_items` · PublicServiceItem[] - `featured` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Sitemap 1 endpoints. HTML: https://developers.keystone.app/api/public/sitemap/ ### GET /api/v1/public/sitemap/current Serve the website sitemap (XML) Operation id: `get_sitemap_current_api_v1_public_sitemap_current_get` Serve the latest persisted sitemap. If nothing is stored yet (cold start), build + persist once and serve. The cold build skips per-file commit lookups (lastmod = now) so the first request never waits on them; a debounced background refresh then fills in the real dates. If that persist can't complete, fall back to building the sitemap on the fly so a real site always gets a valid response. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: string - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Social posts 1 endpoints. HTML: https://developers.keystone.app/api/public/social-posts/ ### GET /api/v1/public/social_posts List published social posts (public) Operation id: `list_social_posts_api_v1_public_social_posts_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicSocialPost__ - `request_id` · string · required - `success` · true - `data` · PublicSocialPost[] · required - `id` · string · required - `platform` · string · required - `content_markdown` · string | null - `posted_at` · string | null - `status` · string | null - `created_at` · string | null - `updated_at` · string | null - `image_urls` · string[] - `video_urls` · string[] - `photo_attachments` · PublicPhotoAttachment[] - `engagement_metrics` · object | null - `external_post_url` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Team members 2 endpoints. HTML: https://developers.keystone.app/api/public/team-members/ ### GET /api/v1/public/team_members List team members Operation id: `list_team_members_api_v1_public_team_members_get` All team members for the business, ordered by display_order. Paginated. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicTeamMember__ - `request_id` · string · required - `success` · true - `data` · PublicTeamMember[] · required - `id` · string · required - `name` · string | null - `role` · string | null - `position` · string | null - `bio_markdown` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `featured` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/public/team_members/featured List featured team members Operation id: `list_team_members_featured_api_v1_public_team_members_featured_get` Team members where is_featured is True. Paginated. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `page` | query | integer | no | | | `per_page` | query | integer | no | | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PublicTeamMember__ - `request_id` · string · required - `success` · true - `data` · PublicTeamMember[] · required - `id` · string · required - `name` · string | null - `role` · string | null - `position` · string | null - `bio_markdown` · string | null - `photo_attachments` · PublicPhotoAttachment[] - `featured` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Public API: Website photos 1 endpoints. HTML: https://developers.keystone.app/api/public/website-photos/ ### GET /api/v1/public/website_photos Get website photos Operation id: `get_website_photos_api_v1_public_website_photos_get` Website photos (logo, favicon, hero, etc.) for the business's website with CDN URLs. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PublicWebsitePhotos_ - `request_id` · string · required - `success` · true - `data` · PublicWebsitePhotos · required: Website photos for GET /website_photos. Config-driven: arbitrary key (website_photos_key) → PublicWebsitePhoto \| null; default config yields same keys as legacy (logo, favicon, hero, etc.). - `stock_photos` · PublicWebsitePhoto[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse # Edits API Sessions, snapshots, staged changesets, apply and undo: the contract behind the MCP server. A small, stable contract for making reviewed changes to a business's information. A caller opens a session on a business it may edit, reads a snapshot, stages a changeset, and applies or reverts it. Every applied change lands in the console's Change History with Undo. The Keystone MCP server calls this surface and nothing else. - Audience: For businesses and partners - Base URL: https://sor.keystone.app - Authentication: Console token, or MCP token with service key. `Authorization: Bearer `. A console user token on its own, or an MCP access token issued by Heimdal together with `X-Internal-Api-Key` holding the MCP server's service key. - Endpoints: 14 in 6 groups - HTML: https://developers.keystone.app/api/edits/ ## Edits API: Business claims 1 endpoints. HTML: https://developers.keystone.app/api/edits/businesses-claim/ ### PUT /api/v1/edits/businesses/{business_id}/claim Claim a business nobody holds, for 24 hours Operation id: `claim_business_api_v1_edits_businesses__business_id__claim_put` The console's claim, for the business picker in Claude too: keystone-mcp passes a claim on only with the card token of the growth partner's click on that card, never on the model's say-so. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ClaimStateData_ - `request_id` · string · required - `success` · true - `data` · ClaimStateData · required: Everything the console's claim control shows. - `business_id` · string · required - `claim` · ClaimData | null - `launched` · boolean · required - `can_claim` · boolean · required - `can_extend` · boolean · required - `server_time` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Edits API: Businesses 1 endpoints. HTML: https://developers.keystone.app/api/edits/businesses/ ### GET /api/v1/edits/businesses Find businesses by name, site or domain, marked with whether the caller can edit them Operation id: `find_businesses_api_v1_edits_businesses_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `q` | query | string | yes | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_BusinessMatchData__ - `request_id` · string · required - `success` · true - `data` · BusinessMatchData[] · required - `business_id` · string · required - `company_name` · string · required - `website_url` · string | null - `city` · string | null - `state` · string | null - `custom_domain` · string | null - `preview_domain` · string | null - `launched` · boolean · required - `claim` · ClaimData | null - `editable` · boolean · required - `locked_reason` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Edits API: Businesses change log 1 endpoints. HTML: https://developers.keystone.app/api/edits/businesses-change-log/ ### GET /api/v1/edits/businesses/{business_id}/change-log Every change to the business's records, by any writer, newest first Operation id: `list_change_log_api_v1_edits_businesses__business_id__change_log_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `before_id` | query | integer \| null | no | | | `changeset_id` | query | string (uuid) \| null | no | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ChangeLogEntryData__ - `request_id` · string · required - `success` · true - `data` · ChangeLogEntryData[] · required - `id` · integer · required - `created_at` · string (date-time) · required - `entity` · string · required - `record_id` · string · required - `op` · string · required - `field_path` · string | null - `label` · string | null - `before_value` · any | null - `after_value` · any | null - `actor_type` · string · required - `actor_id` · string | null - `actor_name` · string | null - `client_id` · string | null - `surface` · string · required - `changeset_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Edits API: Businesses changesets 1 endpoints. HTML: https://developers.keystone.app/api/edits/businesses-changesets/ ### GET /api/v1/edits/businesses/{business_id}/changesets The business's changesets, newest first Operation id: `list_changesets_api_v1_edits_businesses__business_id__changesets_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `before` | query | string (date-time) \| null | no | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ChangesetSummaryData__ - `request_id` · string · required - `success` · true - `data` · ChangesetSummaryData[] · required - `id` · string · required - `status` · string · required - `summary` · string | null - `author_id` · string · required - `author_name` · string | null - `client_id` · string · required - `created_at` · string (date-time) · required - `applied_at` · string (date-time) | null - `reverted_at` · string (date-time) | null - `items` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Edits API: Changesets 5 endpoints. HTML: https://developers.keystone.app/api/edits/changesets/ ### GET /api/v1/edits/changesets/{changeset_id} A changeset and its items Operation id: `get_changeset_api_v1_edits_changesets__changeset_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `changeset_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ChangesetData_ - `request_id` · string · required - `success` · true - `data` · ChangesetData · required - `id` · string · required - `session_id` · string · required - `business_id` · string · required - `company_name` · string · required - `client_id` · string · required - `status` · string · required - `summary` · string | null - `created_at` · string (date-time) · required - `applied_at` · string (date-time) | null - `reverted_at` · string (date-time) | null - `items` · ChangesetItemData[] · required - `replayed` · boolean - `business` · BusinessIdentityData · required: What tells a business apart from others with its name: where it is online and in town, and whether its site is live. Every session, snapshot and changeset carries it, so a card can show which business a change is for. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/edits/changesets/{changeset_id}/apply Apply a staged changeset's ticked items, each only if its value has not moved Operation id: `apply_changeset_api_v1_edits_changesets__changeset_id__apply_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `changeset_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, optional): ApplyChangesetBody | null - `item_ids` · string (uuid)[] | null **Responses** - `200` Successful Response: SuccessResponse_ChangesetData_ - `request_id` · string · required - `success` · true - `data` · ChangesetData · required - `id` · string · required - `session_id` · string · required - `business_id` · string · required - `company_name` · string · required - `client_id` · string · required - `status` · string · required - `summary` · string | null - `created_at` · string (date-time) · required - `applied_at` · string (date-time) | null - `reverted_at` · string (date-time) | null - `items` · ChangesetItemData[] · required - `replayed` · boolean - `business` · BusinessIdentityData · required: What tells a business apart from others with its name: where it is online and in town, and whether its site is live. Every session, snapshot and changeset carries it, so a card can show which business a change is for. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/edits/changesets/{changeset_id}/discard Drop a staged changeset Operation id: `discard_changeset_api_v1_edits_changesets__changeset_id__discard_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `changeset_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ChangesetData_ - `request_id` · string · required - `success` · true - `data` · ChangesetData · required - `id` · string · required - `session_id` · string · required - `business_id` · string · required - `company_name` · string · required - `client_id` · string · required - `status` · string · required - `summary` · string | null - `created_at` · string (date-time) · required - `applied_at` · string (date-time) | null - `reverted_at` · string (date-time) | null - `items` · ChangesetItemData[] · required - `replayed` · boolean - `business` · BusinessIdentityData · required: What tells a business apart from others with its name: where it is online and in town, and whether its site is live. Every session, snapshot and changeset carries it, so a card can show which business a change is for. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/edits/changesets/{changeset_id}/items/{item_id} Tick or untick one staged item Operation id: `select_item_api_v1_edits_changesets__changeset_id__items__item_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `changeset_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, required): ItemSelectionBody - `selected` · boolean · required **Responses** - `200` Successful Response: SuccessResponse_ChangesetItemData_ - `request_id` · string · required - `success` · true - `data` · ChangesetItemData · required - `id` · string · required - `position` · integer · required - `entity` · string · required - `entity_name` · string · required - `op` · string · required - `record_id` · string | null - `record_label` · string | null - `field_path` · string | null - `label` · string | null - `before_value` · any | null - `after_value` · any | null - `flags` · object[] - `selected` · boolean · required - `status` · string · required - `error` · object | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/edits/changesets/{changeset_id}/revert Undo an applied changeset where nothing changed since Operation id: `revert_changeset_api_v1_edits_changesets__changeset_id__revert_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `changeset_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ChangesetData_ - `request_id` · string · required - `success` · true - `data` · ChangesetData · required - `id` · string · required - `session_id` · string · required - `business_id` · string · required - `company_name` · string · required - `client_id` · string · required - `status` · string · required - `summary` · string | null - `created_at` · string (date-time) · required - `applied_at` · string (date-time) | null - `reverted_at` · string (date-time) | null - `items` · ChangesetItemData[] · required - `replayed` · boolean - `business` · BusinessIdentityData · required: What tells a business apart from others with its name: where it is online and in town, and whether its site is live. Every session, snapshot and changeset carries it, so a card can show which business a change is for. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Edits API: Sessions 5 endpoints. HTML: https://developers.keystone.app/api/edits/sessions/ ### POST /api/v1/edits/sessions Open an edit session for one business the caller holds Operation id: `open_session_api_v1_edits_sessions_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, required): OpenSessionBody - `business_id` · string (uuid) · required **Responses** - `201` Successful Response: SuccessResponse_EditSessionData_ - `request_id` · string · required - `success` · true - `data` · EditSessionData · required - `id` · string · required - `business_id` · string · required - `company_name` · string · required - `client_id` · string · required - `status` · string · required - `ended_reason` · string | null - `created_at` · string (date-time) · required - `expires_at` · string (date-time) · required - `business` · BusinessIdentityData · required: What tells a business apart from others with its name: where it is online and in town, and whether its site is live. Every session, snapshot and changeset carries it, so a card can show which business a change is for. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/edits/sessions/{session_id} An edit session, and whether it is still open Operation id: `get_session_api_v1_edits_sessions__session_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `session_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_EditSessionData_ - `request_id` · string · required - `success` · true - `data` · EditSessionData · required - `id` · string · required - `business_id` · string · required - `company_name` · string · required - `client_id` · string · required - `status` · string · required - `ended_reason` · string | null - `created_at` · string (date-time) · required - `expires_at` · string (date-time) · required - `business` · BusinessIdentityData · required: What tells a business apart from others with its name: where it is online and in town, and whether its site is live. Every session, snapshot and changeset carries it, so a card can show which business a change is for. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/edits/sessions/{session_id}/changesets Check proposed changes and stage them as one changeset Operation id: `stage_changeset_api_v1_edits_sessions__session_id__changesets_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `session_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, required): StageChangesetBody - `summary` · string | null - `idempotency_key` · string | null - `items` · ChangeItemIn[] · required - `entity` · string · required - `op` · string · required - `record_id` · string | null - `fields` · object - `expected` · object | null - `version` · string | null **Responses** - `201` Successful Response: SuccessResponse_ChangesetData_ - `request_id` · string · required - `success` · true - `data` · ChangesetData · required - `id` · string · required - `session_id` · string · required - `business_id` · string · required - `company_name` · string · required - `client_id` · string · required - `status` · string · required - `summary` · string | null - `created_at` · string (date-time) · required - `applied_at` · string (date-time) | null - `reverted_at` · string (date-time) | null - `items` · ChangesetItemData[] · required - `replayed` · boolean - `business` · BusinessIdentityData · required: What tells a business apart from others with its name: where it is online and in town, and whether its site is live. Every session, snapshot and changeset carries it, so a card can show which business a change is for. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/edits/sessions/{session_id}/records/{entity}/{record_id} One record in full, with its version (the profile is record_id 'profile') Operation id: `read_record_api_v1_edits_sessions__session_id__records__entity___record_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `session_id` | path | string (uuid) | yes | | | `entity` | path | string | yes | | | `record_id` | path | string | yes | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_RecordData_ - `request_id` · string · required - `success` · true - `data` · RecordData · required - `entity` · string · required - `record_id` · string · required - `label` · string · required - `version` · string · required - `record` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/edits/sessions/{session_id}/snapshot Everything the session's business has: the profile in full, other records trimmed Operation id: `get_snapshot_api_v1_edits_sessions__session_id__snapshot_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `session_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SnapshotData_ - `request_id` · string · required - `success` · true - `data` · SnapshotData · required - `business_id` · string · required - `company_name` · string · required - `profile` · object · required - `profile_version` · string · required - `entities` · EntitySnapshotData[] · required - `business` · BusinessIdentityData · required: What tells a business apart from others with its name: where it is online and in town, and whether its site is live. Every session, snapshot and changeset carries it, so a card can show which business a change is for. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse # Console API Everything the Keystone console does: businesses, contacts, website, ads, social, billing, agents. The surface `go.keystone.app` is built on. Operations are scoped by the caller's console account and its business memberships; staff accounts see every business. This is the largest surface, grouped below by the resource each operation manages. Paths under `/admin` are the console's own; paths under `/businesses` and `/ai` are per-business agent and AI-provider controls. - Audience: Console and first-party apps - Base URL: https://sor.keystone.app - Authentication: Console user token. `Authorization: Bearer `, a Heimdal access token for a console user. - Endpoints: 434 in 59 groups - HTML: https://developers.keystone.app/api/console/ ## Console API: Accounts 11 endpoints. HTML: https://developers.keystone.app/api/console/accounts/ ### POST /api/v1/admin/account-user-invites/{invite_id}/accept Accept a pending account invite for the authenticated user Operation id: `accept_account_invite_api_v1_admin_account_user_invites__invite_id__accept_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `invite_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_AccountInviteConsumeData_ - `request_id` · string · required - `success` · true - `data` · AccountInviteConsumeData · required - `business_id` · string · required - `user` · AccountUserData · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/accounts List admin account summaries Operation id: `list_accounts_api_v1_admin_accounts_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_AccountSummaryData__ - `request_id` · string · required - `success` · true - `data` · AccountSummaryData[] · required - `id` · string · required - `business_name` · string · required - `primary_industry` · string · required - `secondary_industries` · string[] - `user_count` · integer · required - `status` · string · required - `created_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/accounts Create account Operation id: `create_account_api_v1_admin_accounts_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AccountCreateBody - `business_name` · string · required - `primary_industry_id` · string · required - `secondary_industry_ids` · string[] - `primary_user` · AccountPrimaryUserBody | null - `first_name` · string · required - `last_name` · string · required - `email` · string (email) · required **Responses** - `201` Successful Response: SuccessResponse_AccountCreateData_ - `request_id` · string · required - `success` · true - `data` · AccountCreateData · required - `account` · AccountSummaryData · required - `primary_user_id` · string (uuid) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/accounts/{business_id}/users List account users and pending invites Operation id: `list_account_users_api_v1_admin_accounts__business_id__users_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_AccountUserData__ - `request_id` · string · required - `success` · true - `data` · AccountUserData[] · required - `id` · string · required - `kind` · string · required - `first_name` · string - `last_name` · string - `email` · string (email) · required - `phone` · string | null - `role` · string · required - `status` · string · required - `delivery_status` · string | null - `joined_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/accounts/{business_id}/users Invite an account user (pending invite + email) Operation id: `invite_account_user_api_v1_admin_accounts__business_id__users_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AccountUserInviteBody - `email` · string (email) · required - `phone` · string | null - `role` · string **Responses** - `201` Successful Response: SuccessResponse_AccountUserData_ - `request_id` · string · required - `success` · true - `data` · AccountUserData · required - `id` · string · required - `kind` · string · required - `first_name` · string - `last_name` · string - `email` · string (email) · required - `phone` · string | null - `role` · string · required - `status` · string · required - `delivery_status` · string | null - `joined_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/accounts/{business_id}/users/{entry_id} Update an account user or pending invite Operation id: `update_account_user_api_v1_admin_accounts__business_id__users__entry_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string | yes | | | `entry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AccountUserUpdateBody - `first_name` · string · required - `last_name` · string · required - `email` · string (email) · required - `phone` · string | null - `role` · string | null **Responses** - `200` Successful Response: SuccessResponse_AccountUserData_ - `request_id` · string · required - `success` · true - `data` · AccountUserData · required - `id` · string · required - `kind` · string · required - `first_name` · string - `last_name` · string - `email` · string (email) · required - `phone` · string | null - `role` · string · required - `status` · string · required - `delivery_status` · string | null - `joined_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/accounts/{business_id}/users/{entry_id} Delete an account user link or pending invite Operation id: `delete_account_user_api_v1_admin_accounts__business_id__users__entry_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string | yes | | | `entry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/accounts/{business_id}/users/{entry_id}/resend Re-send the invitation email for a pending invite Operation id: `resend_account_invite_api_v1_admin_accounts__business_id__users__entry_id__resend_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string | yes | | | `entry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_AccountUserData_ - `request_id` · string · required - `success` · true - `data` · AccountUserData · required - `id` · string · required - `kind` · string · required - `first_name` · string - `last_name` · string - `email` · string (email) · required - `phone` · string | null - `role` · string · required - `status` · string · required - `delivery_status` · string | null - `joined_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/accounts/from-website Create account from a website URL (streamlined onboarding) Operation id: `create_account_from_website_api_v1_admin_accounts_from_website_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AccountCreateFromWebsiteBody - `website_url` · string · required - `primary_industry_id` · string | null - `secondary_industry_ids` · string[] - `primary_user` · AccountPrimaryUserBody | null - `first_name` · string · required - `last_name` · string · required - `email` · string (email) · required - `user_prompt` · string | null **Responses** - `201` Successful Response: SuccessResponse_AccountCreateFromWebsiteData_ - `request_id` · string · required - `success` · true - `data` · AccountCreateFromWebsiteData · required - `account` · AccountSummaryData · required - `primary_user_id` · string (uuid) | null - `scrape_run_id` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/me/businesses List businesses linked to the authenticated user Operation id: `list_my_businesses_api_v1_admin_me_businesses_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_MyBusinessSummary__ - `request_id` · string · required - `success` · true - `data` · MyBusinessSummary[] · required - `id` · string · required - `business_name` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/me/businesses Create the authenticated user's own business (self-serve onboarding) Operation id: `create_my_business_api_v1_admin_me_businesses_post` Beside GET /me/businesses on purpose: `/api/v1/admin` is the console BFF's prefix, not an admin-only surface, and the router's subscription gate no-ops without a `business_id` in the path — a user with no subscribed business must be able to create one. Always 200; `created` says whether this call made the business or returned the one the caller already had (see the service). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): MyBusinessCreateBody - `business_name` · string · required - `website_url` · string | null **Responses** - `200` Successful Response: SuccessResponse_MyBusinessCreateData_ - `request_id` · string · required - `success` · true - `data` · MyBusinessCreateData · required: ``created`` is false when the caller already had an active business and got that one back — what makes a retry of the create call safe. - `business` · MyBusinessSummary · required - `created` · boolean · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Ads accounts 4 endpoints. HTML: https://developers.keystone.app/api/console/ads-accounts/ ### GET /api/v1/admin/businesses/{business_id}/ads/accounts List ad accounts for the business Operation id: `list_ad_accounts_api_v1_admin_businesses__business_id__ads_accounts_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `external_platform` | query | string \| null | no | | | `include_revoked` | query | boolean | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_AdAccountData__ - `request_id` · string · required - `success` · true - `data` · AdAccountData[] · required - `id` · string (uuid) · required - `businessId` · string (uuid) · required - `externalPlatform` · string · required - `externalPlatformAccountId` · string · required - `externalPlatformBusinessId` · string | null - `defaultPageId` · string | null - `currencyCode` · string · required - `timezoneName` · string · required - `connectedAt` · string (date-time) · required - `lastValidatedAt` · string (date-time) | null - `revokedAt` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/ads/accounts/{ads_account_id}/pages List Pages on a connected ad account (live) Operation id: `list_pages_api_v1_admin_businesses__business_id__ads_accounts__ads_account_id__pages_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `ads_account_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_AdPageData__ - `request_id` · string · required - `success` · true - `data` · AdPageData[] · required - `id` · string · required - `name` · string · required - `category` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/accounts/{ads_account_id}/revoke Revoke an ad-account link (soft). Operation id: `revoke_account_api_v1_admin_businesses__business_id__ads_accounts__ads_account_id__revoke_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `ads_account_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/discovery/import Discover ad accounts + pages from provider; upsert ads_accounts Operation id: `discovery_import_api_v1_admin_businesses__business_id__ads_discovery_import_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): DiscoveryImportBody - `externalPlatform` · string · required - `oauthConnectionId` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_DiscoveryImportData_ - `request_id` · string · required - `success` · true - `data` · DiscoveryImportData · required - `accounts` · AdAccountData[] · required - `pages` · AdPageData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Ads agent 2 endpoints. HTML: https://developers.keystone.app/api/console/ads-agent/ ### GET /api/v1/businesses/{business_id}/ads-agent/config Get effective ads-agent settings for a business Operation id: `get_config_api_v1_businesses__business_id__ads_agent_config_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SimpleAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · SimpleAgentConfigData · required: Shared shape for contacts / ads / listings agents. - `business_id` · string · required - `enabled` · boolean · required - `instructions` · string - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/businesses/{business_id}/ads-agent/config Update ads-agent settings for a business Operation id: `patch_config_api_v1_businesses__business_id__ads_agent_config_patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): SimpleAgentConfigPatchBody - `enabled` · boolean | null - `instructions` · string | null **Responses** - `200` Successful Response: SuccessResponse_SimpleAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · SimpleAgentConfigData · required: Shared shape for contacts / ads / listings agents. - `business_id` · string · required - `enabled` · boolean · required - `instructions` · string - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Ads campaigns 13 endpoints. HTML: https://developers.keystone.app/api/console/ads-campaigns/ ### GET /api/v1/admin/businesses/{business_id}/ads/campaigns List campaigns Operation id: `list_campaigns_api_v1_admin_businesses__business_id__ads_campaigns_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | | | `platform` | query | string \| null | no | | | `q` | query | string \| null | no | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_CampaignData__ - `request_id` · string · required - `success` · true - `data` · CampaignData[] · required - `id` · string (uuid) · required - `name` · string · required - `status` · "draft" | "active" | "paused" | "archived" · required - `platform` · "meta" | "google" · required - `adSetCount` · integer · required - `adUnitCount` · integer · required - `budgetLabel` · string · required - `adSets` · AdSetData[] - `costPerLead` · number | null - `costPerLeadTrendPercent` · integer | null - `reach` · integer | null - `leads` · integer | null - `targetingCity` · string | null - `targetingRadiusMiles` · integer | null - `budgetDailyUsd` · integer | null - `isClientCreated` · boolean | null - `currencyCode` · string - `leadFollowUpEnabled` · boolean - `createdVia` · "manual" | "agent" | "external" - `isExternallyManaged` · boolean - `isManaged` · boolean - `startedAt` · string | null - `objective` · string | null - `budgetLevel` · string - `dailyBudgetMinor` · integer | null - `bidStrategy` · string - `bidAmountMinor` · integer | null - `externalPlatformCampaignId` · string | null - `predecessorCampaignId` · string (uuid) | null - `performanceFlag` · CampaignListFlagSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/campaigns Create a draft campaign Operation id: `create_campaign_api_v1_admin_businesses__business_id__ads_campaigns_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): CampaignCreateBody - `name` · string · required - `platform` · "meta" - `objective` · "OUTCOME_LEADS" | "OUTCOME_TRAFFIC" | "OUTCOME_SALES" - `adsAccountId` · string (uuid) | null - `leadFollowUpEnabled` · boolean **Responses** - `200` Successful Response: SuccessResponse_CampaignData_ - `request_id` · string · required - `success` · true - `data` · CampaignData · required: Mirror of `Campaign` interface in `keystone-console/src/pages/ads/ads-fixtures.ts`, with v2 field renames (D27): ``cpl`` → ``costPerLead``, ``cplTrendPercent`` → ``costPerLeadTrendPercent``, ``ctaType`` → ``callToActionType``. - `id` · string (uuid) · required - `name` · string · required - `status` · "draft" | "active" | "paused" | "archived" · required - `platform` · "meta" | "google" · required - `adSetCount` · integer · required - `adUnitCount` · integer · required - `budgetLabel` · string · required - `adSets` · AdSetData[] - `costPerLead` · number | null - `costPerLeadTrendPercent` · integer | null - `reach` · integer | null - `leads` · integer | null - `targetingCity` · string | null - `targetingRadiusMiles` · integer | null - `budgetDailyUsd` · integer | null - `isClientCreated` · boolean | null - `currencyCode` · string - `leadFollowUpEnabled` · boolean - `createdVia` · "manual" | "agent" | "external" - `isExternallyManaged` · boolean - `isManaged` · boolean - `startedAt` · string | null - `objective` · string | null - `budgetLevel` · string - `dailyBudgetMinor` · integer | null - `bidStrategy` · string - `bidAmountMinor` · integer | null - `externalPlatformCampaignId` · string | null - `predecessorCampaignId` · string (uuid) | null - `performanceFlag` · CampaignListFlagSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id} Get a campaign with embedded ad-sets and units Operation id: `get_campaign_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `refreshFromMeta` | query | boolean | no | When true (default), pull this campaign's latest state from Meta and reconcile SOR before returning — the SAME full sync as the per-campaign Sync button, but due-gated (``ads_campaign_sync_min_interval_seconds``) so opening the page repeatedly doesn't hammer Meta. Set ``refreshFromMeta=false`` to read SOR only. | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CampaignData_ - `request_id` · string · required - `success` · true - `data` · CampaignData · required: Mirror of `Campaign` interface in `keystone-console/src/pages/ads/ads-fixtures.ts`, with v2 field renames (D27): ``cpl`` → ``costPerLead``, ``cplTrendPercent`` → ``costPerLeadTrendPercent``, ``ctaType`` → ``callToActionType``. - `id` · string (uuid) · required - `name` · string · required - `status` · "draft" | "active" | "paused" | "archived" · required - `platform` · "meta" | "google" · required - `adSetCount` · integer · required - `adUnitCount` · integer · required - `budgetLabel` · string · required - `adSets` · AdSetData[] - `costPerLead` · number | null - `costPerLeadTrendPercent` · integer | null - `reach` · integer | null - `leads` · integer | null - `targetingCity` · string | null - `targetingRadiusMiles` · integer | null - `budgetDailyUsd` · integer | null - `isClientCreated` · boolean | null - `currencyCode` · string - `leadFollowUpEnabled` · boolean - `createdVia` · "manual" | "agent" | "external" - `isExternallyManaged` · boolean - `isManaged` · boolean - `startedAt` · string | null - `objective` · string | null - `budgetLevel` · string - `dailyBudgetMinor` · integer | null - `bidStrategy` · string - `bidAmountMinor` · integer | null - `externalPlatformCampaignId` · string | null - `predecessorCampaignId` · string (uuid) | null - `performanceFlag` · CampaignListFlagSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id} Patch campaign name (status changes use dedicated verbs) Operation id: `patch_campaign_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): CampaignPatchBody - `name` · string | null - `leadFollowUpEnabled` · boolean | null - `isManaged` · boolean | null - `objective` · "OUTCOME_LEADS" | "OUTCOME_TRAFFIC" | "OUTCOME_SALES" | null - `budgetLevel` · "campaign" | "ad_set" | null - `dailyBudgetMinor` · integer | null - `bidStrategy` · "LOWEST_COST_WITHOUT_CAP" | "COST_CAP" | "LOWEST_COST_WITH_BID_CAP" | null - `bidAmountMinor` · integer | null **Responses** - `200` Successful Response: SuccessResponse_CampaignData_ - `request_id` · string · required - `success` · true - `data` · CampaignData · required: Mirror of `Campaign` interface in `keystone-console/src/pages/ads/ads-fixtures.ts`, with v2 field renames (D27): ``cpl`` → ``costPerLead``, ``cplTrendPercent`` → ``costPerLeadTrendPercent``, ``ctaType`` → ``callToActionType``. - `id` · string (uuid) · required - `name` · string · required - `status` · "draft" | "active" | "paused" | "archived" · required - `platform` · "meta" | "google" · required - `adSetCount` · integer · required - `adUnitCount` · integer · required - `budgetLabel` · string · required - `adSets` · AdSetData[] - `costPerLead` · number | null - `costPerLeadTrendPercent` · integer | null - `reach` · integer | null - `leads` · integer | null - `targetingCity` · string | null - `targetingRadiusMiles` · integer | null - `budgetDailyUsd` · integer | null - `isClientCreated` · boolean | null - `currencyCode` · string - `leadFollowUpEnabled` · boolean - `createdVia` · "manual" | "agent" | "external" - `isExternallyManaged` · boolean - `isManaged` · boolean - `startedAt` · string | null - `objective` · string | null - `budgetLevel` · string - `dailyBudgetMinor` · integer | null - `bidStrategy` · string - `bidAmountMinor` · integer | null - `externalPlatformCampaignId` · string | null - `predecessorCampaignId` · string (uuid) | null - `performanceFlag` · CampaignListFlagSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id}/archive Archive locally + cascade soft-delete children Operation id: `archive_campaign_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__archive_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CampaignData_ - `request_id` · string · required - `success` · true - `data` · CampaignData · required: Mirror of `Campaign` interface in `keystone-console/src/pages/ads/ads-fixtures.ts`, with v2 field renames (D27): ``cpl`` → ``costPerLead``, ``cplTrendPercent`` → ``costPerLeadTrendPercent``, ``ctaType`` → ``callToActionType``. - `id` · string (uuid) · required - `name` · string · required - `status` · "draft" | "active" | "paused" | "archived" · required - `platform` · "meta" | "google" · required - `adSetCount` · integer · required - `adUnitCount` · integer · required - `budgetLabel` · string · required - `adSets` · AdSetData[] - `costPerLead` · number | null - `costPerLeadTrendPercent` · integer | null - `reach` · integer | null - `leads` · integer | null - `targetingCity` · string | null - `targetingRadiusMiles` · integer | null - `budgetDailyUsd` · integer | null - `isClientCreated` · boolean | null - `currencyCode` · string - `leadFollowUpEnabled` · boolean - `createdVia` · "manual" | "agent" | "external" - `isExternallyManaged` · boolean - `isManaged` · boolean - `startedAt` · string | null - `objective` · string | null - `budgetLevel` · string - `dailyBudgetMinor` · integer | null - `bidStrategy` · string - `bidAmountMinor` · integer | null - `externalPlatformCampaignId` · string | null - `predecessorCampaignId` · string (uuid) | null - `performanceFlag` · CampaignListFlagSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id}/duplicate Deep-copy campaign + sets + units as a new draft Operation id: `duplicate_campaign_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__duplicate_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CampaignData_ - `request_id` · string · required - `success` · true - `data` · CampaignData · required: Mirror of `Campaign` interface in `keystone-console/src/pages/ads/ads-fixtures.ts`, with v2 field renames (D27): ``cpl`` → ``costPerLead``, ``cplTrendPercent`` → ``costPerLeadTrendPercent``, ``ctaType`` → ``callToActionType``. - `id` · string (uuid) · required - `name` · string · required - `status` · "draft" | "active" | "paused" | "archived" · required - `platform` · "meta" | "google" · required - `adSetCount` · integer · required - `adUnitCount` · integer · required - `budgetLabel` · string · required - `adSets` · AdSetData[] - `costPerLead` · number | null - `costPerLeadTrendPercent` · integer | null - `reach` · integer | null - `leads` · integer | null - `targetingCity` · string | null - `targetingRadiusMiles` · integer | null - `budgetDailyUsd` · integer | null - `isClientCreated` · boolean | null - `currencyCode` · string - `leadFollowUpEnabled` · boolean - `createdVia` · "manual" | "agent" | "external" - `isExternallyManaged` · boolean - `isManaged` · boolean - `startedAt` · string | null - `objective` · string | null - `budgetLevel` · string - `dailyBudgetMinor` · integer | null - `bidStrategy` · string - `bidAmountMinor` · integer | null - `externalPlatformCampaignId` · string | null - `predecessorCampaignId` · string (uuid) | null - `performanceFlag` · CampaignListFlagSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id}/launch Validate + push to provider + set Active Operation id: `launch_campaign_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__launch_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `Idempotency-Key` | header | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): CampaignLaunchBody **Responses** - `200` Successful Response: SuccessResponse_CampaignData_ - `request_id` · string · required - `success` · true - `data` · CampaignData · required: Mirror of `Campaign` interface in `keystone-console/src/pages/ads/ads-fixtures.ts`, with v2 field renames (D27): ``cpl`` → ``costPerLead``, ``cplTrendPercent`` → ``costPerLeadTrendPercent``, ``ctaType`` → ``callToActionType``. - `id` · string (uuid) · required - `name` · string · required - `status` · "draft" | "active" | "paused" | "archived" · required - `platform` · "meta" | "google" · required - `adSetCount` · integer · required - `adUnitCount` · integer · required - `budgetLabel` · string · required - `adSets` · AdSetData[] - `costPerLead` · number | null - `costPerLeadTrendPercent` · integer | null - `reach` · integer | null - `leads` · integer | null - `targetingCity` · string | null - `targetingRadiusMiles` · integer | null - `budgetDailyUsd` · integer | null - `isClientCreated` · boolean | null - `currencyCode` · string - `leadFollowUpEnabled` · boolean - `createdVia` · "manual" | "agent" | "external" - `isExternallyManaged` · boolean - `isManaged` · boolean - `startedAt` · string | null - `objective` · string | null - `budgetLevel` · string - `dailyBudgetMinor` · integer | null - `bidStrategy` · string - `bidAmountMinor` · integer | null - `externalPlatformCampaignId` · string | null - `predecessorCampaignId` · string (uuid) | null - `performanceFlag` · CampaignListFlagSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id}/pause Pause on provider + locally Operation id: `pause_campaign_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__pause_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CampaignData_ - `request_id` · string · required - `success` · true - `data` · CampaignData · required: Mirror of `Campaign` interface in `keystone-console/src/pages/ads/ads-fixtures.ts`, with v2 field renames (D27): ``cpl`` → ``costPerLead``, ``cplTrendPercent`` → ``costPerLeadTrendPercent``, ``ctaType`` → ``callToActionType``. - `id` · string (uuid) · required - `name` · string · required - `status` · "draft" | "active" | "paused" | "archived" · required - `platform` · "meta" | "google" · required - `adSetCount` · integer · required - `adUnitCount` · integer · required - `budgetLabel` · string · required - `adSets` · AdSetData[] - `costPerLead` · number | null - `costPerLeadTrendPercent` · integer | null - `reach` · integer | null - `leads` · integer | null - `targetingCity` · string | null - `targetingRadiusMiles` · integer | null - `budgetDailyUsd` · integer | null - `isClientCreated` · boolean | null - `currencyCode` · string - `leadFollowUpEnabled` · boolean - `createdVia` · "manual" | "agent" | "external" - `isExternallyManaged` · boolean - `isManaged` · boolean - `startedAt` · string | null - `objective` · string | null - `budgetLevel` · string - `dailyBudgetMinor` · integer | null - `bidStrategy` · string - `bidAmountMinor` · integer | null - `externalPlatformCampaignId` · string | null - `predecessorCampaignId` · string (uuid) | null - `performanceFlag` · CampaignListFlagSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id}/resume Resume on provider + locally Operation id: `resume_campaign_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__resume_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CampaignData_ - `request_id` · string · required - `success` · true - `data` · CampaignData · required: Mirror of `Campaign` interface in `keystone-console/src/pages/ads/ads-fixtures.ts`, with v2 field renames (D27): ``cpl`` → ``costPerLead``, ``cplTrendPercent`` → ``costPerLeadTrendPercent``, ``ctaType`` → ``callToActionType``. - `id` · string (uuid) · required - `name` · string · required - `status` · "draft" | "active" | "paused" | "archived" · required - `platform` · "meta" | "google" · required - `adSetCount` · integer · required - `adUnitCount` · integer · required - `budgetLabel` · string · required - `adSets` · AdSetData[] - `costPerLead` · number | null - `costPerLeadTrendPercent` · integer | null - `reach` · integer | null - `leads` · integer | null - `targetingCity` · string | null - `targetingRadiusMiles` · integer | null - `budgetDailyUsd` · integer | null - `isClientCreated` · boolean | null - `currencyCode` · string - `leadFollowUpEnabled` · boolean - `createdVia` · "manual" | "agent" | "external" - `isExternallyManaged` · boolean - `isManaged` · boolean - `startedAt` · string | null - `objective` · string | null - `budgetLevel` · string - `dailyBudgetMinor` · integer | null - `bidStrategy` · string - `bidAmountMinor` · integer | null - `externalPlatformCampaignId` · string | null - `predecessorCampaignId` · string (uuid) | null - `performanceFlag` · CampaignListFlagSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id}/sync Pull this one campaign's latest state from Meta and return it Operation id: `sync_campaign_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__sync_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CampaignData_ - `request_id` · string · required - `success` · true - `data` · CampaignData · required: Mirror of `Campaign` interface in `keystone-console/src/pages/ads/ads-fixtures.ts`, with v2 field renames (D27): ``cpl`` → ``costPerLead``, ``cplTrendPercent`` → ``costPerLeadTrendPercent``, ``ctaType`` → ``callToActionType``. - `id` · string (uuid) · required - `name` · string · required - `status` · "draft" | "active" | "paused" | "archived" · required - `platform` · "meta" | "google" · required - `adSetCount` · integer · required - `adUnitCount` · integer · required - `budgetLabel` · string · required - `adSets` · AdSetData[] - `costPerLead` · number | null - `costPerLeadTrendPercent` · integer | null - `reach` · integer | null - `leads` · integer | null - `targetingCity` · string | null - `targetingRadiusMiles` · integer | null - `budgetDailyUsd` · integer | null - `isClientCreated` · boolean | null - `currencyCode` · string - `leadFollowUpEnabled` · boolean - `createdVia` · "manual" | "agent" | "external" - `isExternallyManaged` · boolean - `isManaged` · boolean - `startedAt` · string | null - `objective` · string | null - `budgetLevel` · string - `dailyBudgetMinor` · integer | null - `bidStrategy` · string - `bidAmountMinor` · integer | null - `externalPlatformCampaignId` · string | null - `predecessorCampaignId` · string (uuid) | null - `performanceFlag` · CampaignListFlagSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/campaigns/agent Trigger the campaign-creation agent (workflow engine) Operation id: `trigger_campaign_agent_api_v1_admin_businesses__business_id__ads_campaigns_agent_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `Idempotency-Key` | header | string \| null | no | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): CampaignAgentTriggerBody - `prompt` · string · required - `adsAccountId` · string (uuid) | null - `campaignId` · string (uuid) | null - `adSetId` · string (uuid) | null - `unitId` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_CampaignAgentTriggerData_ - `request_id` · string · required - `success` · true - `data` · CampaignAgentTriggerData · required: Response — FE polls thread/run status from here. - `workflowRunId` · string · required - `threadId` · string · required - `status` · string · required - `assistantReply` · string - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/ads/campaigns/agent/messages List campaign-agent chat transcript Operation id: `list_campaign_agent_messages_api_v1_admin_businesses__business_id__ads_campaigns_agent_messages_get` Return the persisted chat transcript for a thread. The POST relay (``/agent/messages``) only returns the latest assistant reply per turn, so user-typed messages disappear from the FE on refresh. This GET reads the workflow-engine's ``thread_events`` (via control-plane ``/threads/{id}/detail``) and reduces them to the chat-message subset so the FE can render the full transcript and survive page reloads. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `threadId` | query | string | yes | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ChatTranscriptData_ - `request_id` · string · required - `success` · true - `data` · ChatTranscriptData · required: Response wrapper for ``GET /campaigns/agent/messages``. - `threadId` · string · required - `messages` · ChatMessageData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/campaigns/agent/messages Send a follow-up turn on an existing agent thread Operation id: `send_campaign_agent_message_api_v1_admin_businesses__business_id__ads_campaigns_agent_messages_post` Multi-turn relay so the FE doesn't need to talk to the router directly. The initial ``POST /campaigns/agent`` may return an empty ``workflowRunId``; keep POSTing here with the same ``threadId`` until a workflow run is started and a proposal widget appears. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): CampaignAgentMessageBody - `threadId` · string · required - `message` · string · required - `campaignId` · string (uuid) | null - `adSetId` · string (uuid) | null - `unitId` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_CampaignAgentTriggerData_ - `request_id` · string · required - `success` · true - `data` · CampaignAgentTriggerData · required: Response — FE polls thread/run status from here. - `workflowRunId` · string · required - `threadId` · string · required - `status` · string · required - `assistantReply` · string - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Ads performance 1 endpoints. HTML: https://developers.keystone.app/api/console/ads-performance/ ### GET /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id}/performance Campaign daily performance series + ladder flags Operation id: `get_campaign_performance_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__performance_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `windowDays` | query | integer | no | | | `from` | query | string (date) \| null | no | | | `to` | query | string (date) \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CampaignPerformanceData_ - `request_id` · string · required - `success` · true - `data` · CampaignPerformanceData · required - `series` · PerformanceSeriesPoint[] · required - `firstDataDate` · string | null - `lastDataDate` · string | null - `lastSyncAt` · string | null - `tokenFailing` · boolean - `campaignFlag` · FlagSummary | null - `adSetFlags` · AdSetFlagSummary[] - `adUnitFlags` · AdUnitFlagSummary[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Ads refresh recommendations 4 endpoints. HTML: https://developers.keystone.app/api/console/ads-refresh-recommendations/ ### GET /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id}/refresh-recommendations List Refresh Recommendations Operation id: `list_refresh_recommendations_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__refresh_recommendations_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_dict__ - `request_id` · string · required - `success` · true - `data` · object[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/refresh-recommendations/{recommendation_id}/accept Accept Refresh Recommendation Operation id: `accept_refresh_recommendation_api_v1_admin_businesses__business_id__ads_refresh_recommendations__recommendation_id__accept_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `recommendation_id` | path | string (uuid) | yes | | | `Idempotency-Key` | header | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/refresh-recommendations/{recommendation_id}/dismiss Dismiss Refresh Recommendation Operation id: `dismiss_refresh_recommendation_api_v1_admin_businesses__business_id__ads_refresh_recommendations__recommendation_id__dismiss_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `recommendation_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/refresh-recommendations/{recommendation_id}/replace-draft-media Replace Refresh Draft Media Operation id: `replace_refresh_draft_media_api_v1_admin_businesses__business_id__ads_refresh_recommendations__recommendation_id__replace_draft_media_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `recommendation_id` | path | string (uuid) | yes | | | `Idempotency-Key` | header | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ReplaceDraftMediaBody - `photo_ids` · string (uuid)[] · required **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Ads sets 7 endpoints. HTML: https://developers.keystone.app/api/console/ads-sets/ ### GET /api/v1/admin/businesses/{business_id}/ads/ad-sets/{ad_set_id} Get an ad set Operation id: `get_ad_set_api_v1_admin_businesses__business_id__ads_ad_sets__ad_set_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `ad_set_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_AdSetData_ - `request_id` · string · required - `success` · true - `data` · AdSetData · required: Wire shape consumed by Console; mirrors the FE `AdSet` interface with new canonical fields alongside the old string-formatted ones. - `id` · string (uuid) · required - `name` · string · required - `facebookPage` · string - `facebookPageId` · string | null - `destination` · "website" | "facebook-native" | "instant_form" - `websiteUrl` · string - `dailyBudget` · string - `dailyBudgetMinor` · integer | null - `location` · string - `radius` · string - `radiusMeters` · integer | null - `units` · AdUnitData[] - `currencyCode` · string - `isDraft` · boolean - `externalPlatformAdsetId` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/ads/ad-sets/{ad_set_id} Patch ad set fields (mutable; audit-logged) Operation id: `patch_ad_set_api_v1_admin_businesses__business_id__ads_ad_sets__ad_set_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `ad_set_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AdSetPatchBody - `name` · string | null - `facebookPageId` · string | null - `destination` · "website" | "facebook-native" | "instant_form" | null - `destinationUrl` · string | null - `dailyBudgetMinor` · integer | null - `addressLine` · string | null - `lat` · number | null - `lng` · number | null - `radiusMeters` · integer | null **Responses** - `200` Successful Response: SuccessResponse_AdSetData_ - `request_id` · string · required - `success` · true - `data` · AdSetData · required: Wire shape consumed by Console; mirrors the FE `AdSet` interface with new canonical fields alongside the old string-formatted ones. - `id` · string (uuid) · required - `name` · string · required - `facebookPage` · string - `facebookPageId` · string | null - `destination` · "website" | "facebook-native" | "instant_form" - `websiteUrl` · string - `dailyBudget` · string - `dailyBudgetMinor` · integer | null - `location` · string - `radius` · string - `radiusMeters` · integer | null - `units` · AdUnitData[] - `currencyCode` · string - `isDraft` · boolean - `externalPlatformAdsetId` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/ads/ad-sets/{ad_set_id} Soft-delete ad set Operation id: `delete_ad_set_api_v1_admin_businesses__business_id__ads_ad_sets__ad_set_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `ad_set_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/ad-sets/{ad_set_id}/generate-ad-copy DEPRECATED: LLM-generate ad copy (use the unit-level generate-copy) Operation id: `generate_ad_copy_api_v1_admin_businesses__business_id__ads_ad_sets__ad_set_id__generate_ad_copy_post` DEPRECATED (KS-2013): copy is per-ad. Delegates to the set's FIRST unit; 409 ``AD_SET_HAS_NO_UNITS`` when the set has none. Use ``POST /businesses/{b}/ads/units/{unit_id}/generate-copy`` instead. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `ad_set_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_AdCopyGenerationData_ - `request_id` · string · required - `success` · true - `data` · AdCopyGenerationData · required: Response body for ``POST /ad-sets/{id}/generate-ad-copy``. - `adCopy` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id}/ad-sets List ad sets in a campaign Operation id: `list_ad_sets_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__ad_sets_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_AdSetData__ - `request_id` · string · required - `success` · true - `data` · AdSetData[] · required - `id` · string (uuid) · required - `name` · string · required - `facebookPage` · string - `facebookPageId` · string | null - `destination` · "website" | "facebook-native" | "instant_form" - `websiteUrl` · string - `dailyBudget` · string - `dailyBudgetMinor` · integer | null - `location` · string - `radius` · string - `radiusMeters` · integer | null - `units` · AdUnitData[] - `currencyCode` · string - `isDraft` · boolean - `externalPlatformAdsetId` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/campaigns/{campaign_id}/ad-sets Create an ad set Operation id: `create_ad_set_api_v1_admin_businesses__business_id__ads_campaigns__campaign_id__ad_sets_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `campaign_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AdSetCreateBody - `name` · string | null - `facebookPageId` · string | null - `destination` · "website" | "facebook-native" | "instant_form" - `destinationUrl` · string | null - `dailyBudgetMinor` · integer | null - `addressLine` · string | null - `lat` · number | null - `lng` · number | null - `radiusMeters` · integer | null - `currencyCode` · string **Responses** - `200` Successful Response: SuccessResponse_AdSetData_ - `request_id` · string · required - `success` · true - `data` · AdSetData · required: Wire shape consumed by Console; mirrors the FE `AdSet` interface with new canonical fields alongside the old string-formatted ones. - `id` · string (uuid) · required - `name` · string · required - `facebookPage` · string - `facebookPageId` · string | null - `destination` · "website" | "facebook-native" | "instant_form" - `websiteUrl` · string - `dailyBudget` · string - `dailyBudgetMinor` · integer | null - `location` · string - `radius` · string - `radiusMeters` · integer | null - `units` · AdUnitData[] - `currencyCode` · string - `isDraft` · boolean - `externalPlatformAdsetId` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/generate-ad-copy LLM-generate ad copy from wizard local state (no persistence) Operation id: `generate_ad_copy_pre_draft_api_v1_admin_businesses__business_id__ads_generate_ad_copy_post` Pre-draft variant of ``generate-ad-copy``. Used by the New Campaign wizard before any ad set row exists — same generation logic, same business-profile grounding, just sourced from the request body instead of a DB read. No row writes. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AdCopyPreDraftBody - `destination` · "website" | "facebook-native" | "instant_form" | null - `destinationUrl` · string | null - `addressLine` · string | null - `lat` · number | null - `lng` · number | null - `radiusMeters` · integer | null - `dailyBudgetMinor` · integer | null - `currencyCode` · string | null - `campaignName` · string | null - `userPrompt` · string | null **Responses** - `200` Successful Response: SuccessResponse_AdCopyGenerationData_ - `request_id` · string · required - `success` · true - `data` · AdCopyGenerationData · required: Response body for ``POST /ad-sets/{id}/generate-ad-copy``. - `adCopy` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Ads sync 2 endpoints. HTML: https://developers.keystone.app/api/console/ads-sync/ ### POST /api/v1/admin/businesses/{business_id}/ads/accounts/{ads_account_id}/sync Sync this ad account from Meta now (entities + insights) Operation id: `sync_account_api_v1_admin_businesses__business_id__ads_accounts__ads_account_id__sync_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `ads_account_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_AdAccountData_ - `request_id` · string · required - `success` · true - `data` · AdAccountData · required - `id` · string (uuid) · required - `businessId` · string (uuid) · required - `externalPlatform` · string · required - `externalPlatformAccountId` · string · required - `externalPlatformBusinessId` · string | null - `defaultPageId` · string | null - `currencyCode` · string · required - `timezoneName` · string · required - `connectedAt` · string (date-time) · required - `lastValidatedAt` · string (date-time) | null - `revokedAt` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/ads/accounts/{ads_account_id}/sync-attempts List sync attempts for an ad account Operation id: `list_sync_attempts_api_v1_admin_businesses__business_id__ads_accounts__ads_account_id__sync_attempts_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `ads_account_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_AdSyncAttemptData__ - `request_id` · string · required - `success` · true - `data` · AdSyncAttemptData[] · required - `id` · string (uuid) · required - `businessId` · string (uuid) · required - `adsAccountId` · string (uuid) · required - `campaignId` · string (uuid) | null - `syncType` · "full" | "entities" | "insights" | "insights_daily" · required - `status` · "pending" | "running" | "partial" | "failed" | "succeeded" · required - `attemptNumber` · integer · required - `parentAttemptId` · string (uuid) | null - `startedAt` · string (date-time) | null - `finishedAt` · string (date-time) | null - `error` · object | null - `createdAt` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Ads units 5 endpoints. HTML: https://developers.keystone.app/api/console/ads-units/ ### POST /api/v1/admin/businesses/{business_id}/ads/ad-sets/{ad_set_id}/units Create an ad unit (creative + unit row, single transaction) Operation id: `create_ad_unit_api_v1_admin_businesses__business_id__ads_ad_sets__ad_set_id__units_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `ad_set_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AdUnitCreateBody - `photoIds` · string (uuid)[] | null - `photoId` · string (uuid) | null - `enabled` · boolean - `displayIndex` · integer | null - `name` · string | null - `primaryText` · string | null - `headline` · string | null - `description` · string | null - `destinationUrl` · string | null - `callToActionType` · string | null - `urlParams` · string | null **Responses** - `200` Successful Response: SuccessResponse_AdUnitData_ - `request_id` · string · required - `success` · true - `data` · AdUnitData · required - `id` · string (uuid) · required - `name` · string · required - `primaryText` · string | null - `headline` · string | null - `description` · string | null - `destinationUrl` · string | null - `callToActionType` · string | null - `urlParams` · string | null - `effectiveHeadline` · string - `effectivePrimaryText` · string - `effectiveDescription` · string - `effectiveCta` · string - `effectiveLink` · string - `imageUrls` · string[] - `status` · "leading" | "neutral" | "lagging" | "off" - `enabled` · boolean - `isDraft` · boolean - `externalPlatformAdId` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/ads/units/{unit_id} Toggle, swap photos, reorder Operation id: `patch_ad_unit_api_v1_admin_businesses__business_id__ads_units__unit_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `unit_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AdUnitPatchBody - `enabled` · boolean | null - `photoIds` · string (uuid)[] | null - `photoId` · string (uuid) | null - `displayIndex` · integer | null - `name` · string | null - `primaryText` · string | null - `headline` · string | null - `description` · string | null - `destinationUrl` · string | null - `callToActionType` · string | null - `urlParams` · string | null **Responses** - `200` Successful Response: SuccessResponse_AdUnitData_ - `request_id` · string · required - `success` · true - `data` · AdUnitData · required - `id` · string (uuid) · required - `name` · string · required - `primaryText` · string | null - `headline` · string | null - `description` · string | null - `destinationUrl` · string | null - `callToActionType` · string | null - `urlParams` · string | null - `effectiveHeadline` · string - `effectivePrimaryText` · string - `effectiveDescription` · string - `effectiveCta` · string - `effectiveLink` · string - `imageUrls` · string[] - `status` · "leading" | "neutral" | "lagging" | "off" - `enabled` · boolean - `isDraft` · boolean - `externalPlatformAdId` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/ads/units/{unit_id} Soft-delete an ad unit Operation id: `delete_ad_unit_api_v1_admin_businesses__business_id__ads_units__unit_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `unit_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/units/{unit_id}/duplicate Duplicate an ad unit (photos + copy overrides; clone starts disabled) Operation id: `duplicate_ad_unit_api_v1_admin_businesses__business_id__ads_units__unit_id__duplicate_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `unit_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `201` Successful Response: SuccessResponse_AdUnitData_ - `request_id` · string · required - `success` · true - `data` · AdUnitData · required - `id` · string (uuid) · required - `name` · string · required - `primaryText` · string | null - `headline` · string | null - `description` · string | null - `destinationUrl` · string | null - `callToActionType` · string | null - `urlParams` · string | null - `effectiveHeadline` · string - `effectivePrimaryText` · string - `effectiveDescription` · string - `effectiveCta` · string - `effectiveLink` · string - `imageUrls` · string[] - `status` · "leading" | "neutral" | "lagging" | "off" - `enabled` · boolean - `isDraft` · boolean - `externalPlatformAdId` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/ads/units/{unit_id}/generate-copy LLM-generate primary text for one ad unit (KS-2013 per-ad copy) Operation id: `generate_unit_copy_api_v1_admin_businesses__business_id__ads_units__unit_id__generate_copy_post` Unit-scoped successor of ``POST /ad-sets/{id}/generate-ad-copy``: grounded in the unit's creative (existing primary text, CTA, link) plus its set/campaign/business context. Same response shape — the FE drops the text into the unit's primary-text field. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `unit_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_AdCopyGenerationData_ - `request_id` · string · required - `success` · true - `data` · AdCopyGenerationData · required: Response body for ``POST /ad-sets/{id}/generate-ad-copy``. - `adCopy` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: AI providers 6 endpoints. HTML: https://developers.keystone.app/api/console/ai-providers/ ### GET /api/v1/admin/ai/providers List AI provider configs Operation id: `list_providers_api_v1_admin_ai_providers_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_AiProviderData__ - `request_id` · string · required - `success` · true - `data` · AiProviderData[] · required - `id` · string · required - `name` · string · required - `provider` · string · required - `model` · string · required - `max_tokens` · integer · required - `temperature` · number · required - `cache_enabled` · boolean · required - `metadata` · object - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/ai/providers/{name} Get AI provider config by name Operation id: `get_provider_api_v1_admin_ai_providers__name__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `name` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_AiProviderData_ - `request_id` · string · required - `success` · true - `data` · AiProviderData · required: Response shape for /ai/providers. - `id` · string · required - `name` · string · required - `provider` · string · required - `model` · string · required - `max_tokens` · integer · required - `temperature` · number · required - `cache_enabled` · boolean · required - `metadata` · object - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/ai/providers/{name} Update AI provider config Operation id: `patch_provider_api_v1_admin_ai_providers__name__patch` Partial update keyed by `name`. Cache invalidates immediately for this process; other replicas pick up changes within ~60s. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `name` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AiProviderPatchBody - `provider` · string | null - `model` · string | null - `max_tokens` · integer | null - `temperature` · number | null - `cache_enabled` · boolean | null - `metadata` · object | null **Responses** - `200` Successful Response: SuccessResponse_AiProviderData_ - `request_id` · string · required - `success` · true - `data` · AiProviderData · required: Response shape for /ai/providers. - `id` · string · required - `name` · string · required - `provider` · string · required - `model` · string · required - `max_tokens` · integer · required - `temperature` · number · required - `cache_enabled` · boolean · required - `metadata` · object - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/ai/providers List AI provider configs Operation id: `list_providers_api_v1_ai_providers_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_AiProviderData__ - `request_id` · string · required - `success` · true - `data` · AiProviderData[] · required - `id` · string · required - `name` · string · required - `provider` · string · required - `model` · string · required - `max_tokens` · integer · required - `temperature` · number · required - `cache_enabled` · boolean · required - `metadata` · object - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/ai/providers/{name} Get AI provider config by name Operation id: `get_provider_api_v1_ai_providers__name__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `name` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_AiProviderData_ - `request_id` · string · required - `success` · true - `data` · AiProviderData · required: Response shape for /ai/providers. - `id` · string · required - `name` · string · required - `provider` · string · required - `model` · string · required - `max_tokens` · integer · required - `temperature` · number · required - `cache_enabled` · boolean · required - `metadata` · object - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/ai/providers/{name} Update AI provider config Operation id: `patch_provider_api_v1_ai_providers__name__patch` Partial update keyed by `name`. Cache invalidates immediately for this process; other replicas pick up changes within ~60s. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `name` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AiProviderPatchBody - `provider` · string | null - `model` · string | null - `max_tokens` · integer | null - `temperature` · number | null - `cache_enabled` · boolean | null - `metadata` · object | null **Responses** - `200` Successful Response: SuccessResponse_AiProviderData_ - `request_id` · string · required - `success` · true - `data` · AiProviderData · required: Response shape for /ai/providers. - `id` · string · required - `name` · string · required - `provider` · string · required - `model` · string · required - `max_tokens` · integer · required - `temperature` · number · required - `cache_enabled` · boolean · required - `metadata` · object - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Billing 11 endpoints. HTML: https://developers.keystone.app/api/console/billing/ ### GET /api/v1/admin/billing/{business_id}/auto-reload Get a business's auto-reload config Operation id: `get_auto_reload_api_v1_admin_billing__business_id__auto_reload_get` Read auto-reload settings + spent-this-month / failure counters. Payments returns a default disabled config when unset; a payments outage degrades to 503. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_AutoReloadData_ - `request_id` · string · required - `success` · true - `data` · AutoReloadData · required: A business's auto-reload settings + counters. Money fields are **minor units**; ``trigger_credits`` is a decimal string (credits). The console shows ``spent_this_month`` and a paused/needs-attention state from ``consecutive_failures``. - `enabled` · boolean · required - `reload_amount` · integer | null - `trigger_credits` · string | null - `monthly_cap_amount` · integer | null - `spent_this_month` · integer - `consecutive_failures` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/billing/{business_id}/auto-reload Save a business's auto-reload config Operation id: `put_auto_reload_api_v1_admin_billing__business_id__auto_reload_put` Save auto-reload config. Real membership required — this standing authority charges the business's card unattended, so it is not something support sets on their behalf. A payments business 4xx is surfaced verbatim (``_api_error``); transport / 5xx → 503. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AutoReloadUpdateBody - `enabled` · boolean · required - `reload_amount` · integer | null - `trigger_credits` · string | null - `monthly_cap_amount` · integer | null **Responses** - `200` Successful Response: SuccessResponse_AutoReloadData_ - `request_id` · string · required - `success` · true - `data` · AutoReloadData · required: A business's auto-reload settings + counters. Money fields are **minor units**; ``trigger_credits`` is a decimal string (credits). The console shows ``spent_this_month`` and a paused/needs-attention state from ``consecutive_failures``. - `enabled` · boolean · required - `reload_amount` · integer | null - `trigger_credits` · string | null - `monthly_cap_amount` · integer | null - `spent_this_month` · integer - `consecutive_failures` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/billing/{business_id}/balance Get a business's credit balance (used-of-total) Operation id: `get_billing_balance_api_v1_admin_billing__business_id__balance_get` Credit balance for the used-of-total bar. The console aggregates the lots (Σ usage / Σ amount); no per-lot breakdown is rendered in A3. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BillingBalanceData_ - `request_id` · string · required - `success` · true - `data` · BillingBalanceData · required: A business's credit balance. The console aggregates the lots to used-of-total (Σ usage / Σ amount) — no per-lot breakdown is rendered in A3. - `spendable_credits` · string · required - `debt_credits` · string · required - `lots` · BillingBalanceLot[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/billing/{business_id}/checkout-session Mint a subscription Checkout session URL Operation id: `create_checkout_session_api_v1_admin_billing__business_id__checkout_session_post` Mint a Stripe Checkout URL to subscribe. Gated at ``user`` (a business write — excludes read-only ``viewer`` members; ``manager``/``developer``/``admin`` are Keystone global staff). No subscription gate, so a not-subscribed member can actually subscribe. ``email``/``name`` label the Stripe customer. **Real membership required** (``staff_bypass=False``, 2026-09-03) — staff do *not* bypass it here as they do elsewhere. This is the mint that creates the business's Stripe customer, once, from ``body.email``; a staff-initiated subscribe would pin a Keystone address as the customer's permanent billing contact and divert every receipt and dunning email to it. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): CheckoutSessionBody - `plan_name` · string · required - `email` · string (email) · required - `name` · string | null - `success_url` · string · required - `cancel_url` · string · required - `promotion_code_id` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_CheckoutSessionData_ - `request_id` · string · required - `success` · true - `data` · CheckoutSessionData · required - `checkout_url` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/billing/{business_id}/grants Grant credits to a subscribed business (platform admin) Operation id: `grant_credits_api_v1_admin_billing__business_id__grants_post` Grant credits to a business that has a subscription (credit-grants-admin-design.md G2, G9–G11). "Subscribed" means *has a subscription* — `active`, `grace`, `suspended` or `canceled` — not *is currently spendable*: a make-good to a lapsed customer is the case support hits most, and their credits do not vanish when they lapse. payments itself will grant to any id, so this is BFF policy, like `onboard-free`'s gate. The check runs **before** payments is called, so a refusal costs one read. `retryable` (payments unreadable) answers **503**, and so does `status == "unknown"` on its own: it travels with `retryable` on every unreadable path but one (a 200 whose body lacks `status` is defaulted to `unknown` without it), and neither may fall through to a grant. The two checks are independent — an unreadable status is never spelled `none`, so their order does not matter (verified by mutation, 2026-09-21); what matters is that `unknown` is named explicitly. Then `none` → **409 `NOT_SUBSCRIBED`** — 409, not 402: the console reads 402 as "pay to proceed", the wrong instruction for staff acting on someone else's account. No sor-side business lookup, unlike `onboard-free`: a business sor does not know has no billing account, reads as `none`, and is refused here — and payments cannot orphan anything because it refuses without an account. `actor` is derived from the JWT; `source` is always `manual`; no `price_book_id` is sent (G4). payments' business answers pass through verbatim with `details`: **409 `GRANT_AMOUNT_MISMATCH`** (the same key with a different amount — `details.lot_id` / `details.granted`), **400 `NO_PRICE_BOOK_FOR_GRANT`** (a canceled business whose lots have all expired), **400 `VALIDATION_ERROR`**. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): GrantBody - `credits` · string · required - `reason` · string · required - `idempotency_key` · string · required **Responses** - `200` Successful Response: SuccessResponse_GrantData_ - `request_id` · string · required - `success` · true - `data` · GrantData · required: The lot minted — or, on a replay of the same key, the lot minted the first time — and `granted` as the caller spelled it. - `lot_id` · string (uuid) · required - `granted` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/billing/{business_id}/onboard-free Put a business on the $0 free plan (platform admin) Operation id: `onboard_free_plan_api_v1_admin_billing__business_id__onboard_free_post` Create an `active` account and a `$0` subscription for a business that has never been billed — no Stripe object, no credit grant, usage rates to 0. `plan_name` says which free plan — the catalogue can carry more than one, so the card the admin pressed is the plan that gets attached. `actor` is *not* in the body on purpose: it is derived from the caller's JWT, so the audit row on payments cannot be spoofed or left blank by whoever calls this. The plan is validated by payments, not here: it owns the catalogue, and duplicating the `free`-kind check in the BFF would mean two places to keep right. Payments' business answers pass through verbatim: **409** the business already has a billing account (naming the plan it is on) or has reached Stripe, **404** no such active plan, **400** the named plan is not a free one. Separately **404** when sor has no such business — payments has no `businesses` table and deliberately no foreign key to one, so it would happily onboard a typo'd id and leave an orphan billing account nothing would ever surface. sor owns that table; the check belongs here. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): OnboardFreeBody - `plan_name` · string · required **Responses** - `200` Successful Response: SuccessResponse_OnboardFreeData_ - `request_id` · string · required - `success` · true - `data` · OnboardFreeData · required: Result of putting a business on the `$0` free plan (platform-admin action). - `business_id` · string (uuid) · required - `status` · string · required - `plan_name` · string · required - `plan_kind` · string · required - `subscription_id` · string (uuid) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/billing/{business_id}/plans List the self-serve plan catalogue Operation id: `get_plan_catalog_api_v1_admin_billing__business_id__plans_get` The plan catalogue for the not-subscribed Billing screen (one card per plan). The catalogue is global; ``business_id`` scopes membership auth only (like the other billing reads). A payments outage degrades to 503 ``BILLING_UNAVAILABLE`` — the console just can't render cards. **A platform admin also gets the `$0` free plan**, because only they can put a business on it (``POST …/onboard-free`` below). The flag is derived from the caller's role and is deliberately **not** a query parameter: the free plan has no Stripe price, so a customer who saw that card and pressed Subscribe would get a 503 — and it sorts first, being cheapest. Making it client-settable would put that failure one crafted request away. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PlanCatalogData_ - `request_id` · string · required - `success` · true - `data` · PlanCatalogData · required: The plan catalogue shown on the not-subscribed Billing screen (one card per plan). Contains the `$0` free plan **only** for a platform admin — see the route. `promotions` is the discount codes this business may redeem, as one flat list rather than a copy nested under each plan: a code that applies to several plans would otherwise be repeated, and the console has to filter by name anyway. It is `[]` when there are none **and** when the offers call fails — the cards matter more than the dropdown, so the catalogue never fails on their account. - `plans` · PlanCatalogItem[] · required - `promotions` · PromotionOfferData[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/billing/{business_id}/portal-session Mint a Customer Portal session URL Operation id: `create_portal_session_api_v1_admin_billing__business_id__portal_session_post` Mint a Stripe Customer Portal URL to manage the subscription / payment method. Gated at ``user`` (a business write) — same tier as subscribing, and the same real-membership requirement: the Portal lets the holder change the card and cancel, which is the business's call, not support's. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): PortalSessionBody - `return_url` · string · required **Responses** - `200` Successful Response: SuccessResponse_PortalSessionData_ - `request_id` · string · required - `success` · true - `data` · PortalSessionData · required - `portal_url` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/billing/{business_id}/reactivation-link Mint the reactivation payment link for a delinquent subscription Operation id: `create_reactivation_link_api_v1_admin_billing__business_id__reactivation_link_post` The hosted-invoice URL whose payment revives a grace/suspended subscription — payments resolves the sub's LITERAL latest invoice (finalizing it first when dormant cycles left it a draft; door 1, 2026-08). Payments' real 4xx (account not delinquent / latest invoice unpayable) passes through verbatim so the console can render it. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ReactivationLinkData_ - `request_id` · string · required - `success` · true - `data` · ReactivationLinkData · required: The hosted-invoice URL whose payment revives a delinquent (grace/suspended) subscription. - `reactivation_url` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/billing/{business_id}/recharge-session Mint a one-time credit-recharge Checkout session URL Operation id: `create_recharge_session_api_v1_admin_billing__business_id__recharge_session_post` Mint a recharge Checkout URL (``amount`` = minor units; payments enforces $5–$10k + requires an in-force subscription). Real membership required — a support click here charges the customer's saved card for real. A payments **402** (no subscription) / **400** (bad amount) is surfaced verbatim; transport / 5xx → 503. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): RechargeSessionBody - `amount` · integer · required - `success_url` · string · required - `cancel_url` · string · required **Responses** - `200` Successful Response: SuccessResponse_RechargeSessionData_ - `request_id` · string · required - `success` · true - `data` · RechargeSessionData · required - `checkout_url` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/billing/{business_id}/status Get a business's billing/entitlement status Operation id: `get_billing_status_api_v1_admin_billing__business_id__status_get` Read plan + entitlement for the Billing overview. ``get_access_status`` is fail-closed for the enforcement gate, but for *display* an unreadable status must not masquerade as suspended — so a ``retryable`` (payments-unreachable) result surfaces as 503, not a fake status. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BillingStatusData_ - `request_id` · string · required - `success` · true - `data` · BillingStatusData · required: A business's entitlement snapshot for the console. ``access`` is true only for active/grace; the console reads ``status`` for the grace/suspended/must-subscribe UX and ``plan_name`` / ``current_period_end`` for the overview. - `status` · string · required - `access` · boolean · required - `ops_blocked` · boolean - `plan_name` · string | null - `plan_kind` · string | null - `current_period_end` · string | null - `cancel_at_period_end` · boolean - `grace_days_remaining` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Blog agent 2 endpoints. HTML: https://developers.keystone.app/api/console/blog-agent/ ### GET /api/v1/businesses/{business_id}/blog-agent/config Get effective blog-agent settings for a business Operation id: `get_config_api_v1_businesses__business_id__blog_agent_config_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BlogAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · BlogAgentConfigData · required: Blog-agent settings — custom instructions only (scheduler is separate). - `business_id` · string · required - `instructions` · string - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/businesses/{business_id}/blog-agent/config Update blog-agent settings for a business Operation id: `patch_config_api_v1_businesses__business_id__blog_agent_config_patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): BlogAgentConfigPatchBody - `instructions` · string | null **Responses** - `200` Successful Response: SuccessResponse_BlogAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · BlogAgentConfigData · required: Blog-agent settings — custom instructions only (scheduler is separate). - `business_id` · string · required - `instructions` · string - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Blog auto schedule 3 endpoints. HTML: https://developers.keystone.app/api/console/blog-auto-schedule/ ### GET /api/v1/businesses/{business_id}/blog_posts/auto_schedule Get business-level auto-schedule Operation id: `get_business_auto_schedule_api_v1_businesses__business_id__blog_posts_auto_schedule_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BlogAutoScheduleData_ - `request_id` · string · required - `success` · true - `data` · BlogAutoScheduleData · required - `id` · string · required - `business_id` · string · required - `auto_publish` · boolean · required - `frequency_type` · string · required - `is_enabled` · boolean · required - `require_approval` · boolean - `target_word_count` · integer - `timezone` · string - `time_of_day` · string - `next_run_at` · integer | null - `last_run_at` · integer | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/businesses/{business_id}/blog_posts/auto_schedule Create or update business-level auto-schedule Operation id: `save_business_auto_schedule_api_v1_businesses__business_id__blog_posts_auto_schedule_put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): BlogAutoScheduleBody - `auto_publish` · boolean - `frequency_type` · string - `is_enabled` · boolean **Responses** - `200` Successful Response: SuccessResponse_BlogAutoScheduleData_ - `request_id` · string · required - `success` · true - `data` · BlogAutoScheduleData · required - `id` · string · required - `business_id` · string · required - `auto_publish` · boolean · required - `frequency_type` · string · required - `is_enabled` · boolean · required - `require_approval` · boolean - `target_word_count` · integer - `timezone` · string - `time_of_day` · string - `next_run_at` · integer | null - `last_run_at` · integer | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/businesses/{business_id}/blog_posts/auto_schedule Delete business-level auto-schedule Operation id: `delete_business_auto_schedule_api_v1_businesses__business_id__blog_posts_auto_schedule_delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Blog posts 7 endpoints. HTML: https://developers.keystone.app/api/console/blog-posts/ ### GET /api/v1/admin/businesses/{business_id}/blog_posts List blog posts for a business Operation id: `list_blog_posts_api_v1_admin_businesses__business_id__blog_posts_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string | no | Status bucket filter: all\|draft\|published\|pending\|archived | | `search` | query | string \| null | no | Search title and excerpt | | `cursor` | query | string \| null | no | Pagination cursor | | `limit` | query | integer | no | Page size | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_BlogPostListItemData__ - `request_id` · string · required - `success` · true - `data` · BlogPostListItemData[] · required - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `status` · string · required - `status_bucket` · string · required - `publish_date` · string | null - `thumbnail_photo_id` · string | null - `author_preview` · BlogPostAuthorPreviewData[] - `excerpt` · string | null - `is_featured` · boolean - `media_status` · string | null - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/blog_posts Create a blog post Operation id: `create_blog_post_api_v1_admin_businesses__business_id__blog_posts_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): BlogPostWriteBody - `title` · string · required - `slug` · string · required - `status` · string · required - `publish_date` · string | null - `excerpt` · string | null - `content_markdown` · string | null - `content_html` · string | null - `tags` · string[] - `photo_ids` · string[] - `is_featured` · boolean - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] - `author_team_member_ids` · string[] - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null - `brief` · object | null **Responses** - `201` Successful Response: SuccessResponse_BlogPostData_ - `request_id` · string · required - `success` · true - `data` · BlogPostData · required - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `status` · string · required - `status_bucket` · string · required - `publish_date` · string | null - `excerpt` · string | null - `content_markdown` · string | null - `content_html` · string | null - `tags` · string[] | null - `photo_ids` · string[] | null - `is_featured` · boolean - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] | null - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null - `brief` · object | null - `author_preview` · BlogPostAuthorPreviewData[] - `authors` · BlogPostAuthorData[] - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/blog_posts/{blog_post_id} Get a single blog post Operation id: `get_blog_post_api_v1_admin_businesses__business_id__blog_posts__blog_post_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `blog_post_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BlogPostData_ - `request_id` · string · required - `success` · true - `data` · BlogPostData · required - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `status` · string · required - `status_bucket` · string · required - `publish_date` · string | null - `excerpt` · string | null - `content_markdown` · string | null - `content_html` · string | null - `tags` · string[] | null - `photo_ids` · string[] | null - `is_featured` · boolean - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] | null - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null - `brief` · object | null - `author_preview` · BlogPostAuthorPreviewData[] - `authors` · BlogPostAuthorData[] - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/blog_posts/{blog_post_id} Update a blog post Operation id: `update_blog_post_api_v1_admin_businesses__business_id__blog_posts__blog_post_id__put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `blog_post_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): BlogPostWriteBody - `title` · string · required - `slug` · string · required - `status` · string · required - `publish_date` · string | null - `excerpt` · string | null - `content_markdown` · string | null - `content_html` · string | null - `tags` · string[] - `photo_ids` · string[] - `is_featured` · boolean - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] - `author_team_member_ids` · string[] - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null - `brief` · object | null **Responses** - `200` Successful Response: SuccessResponse_BlogPostData_ - `request_id` · string · required - `success` · true - `data` · BlogPostData · required - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `status` · string · required - `status_bucket` · string · required - `publish_date` · string | null - `excerpt` · string | null - `content_markdown` · string | null - `content_html` · string | null - `tags` · string[] | null - `photo_ids` · string[] | null - `is_featured` · boolean - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] | null - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null - `brief` · object | null - `author_preview` · BlogPostAuthorPreviewData[] - `authors` · BlogPostAuthorData[] - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/blog_posts/{blog_post_id} Delete a blog post Operation id: `delete_blog_post_api_v1_admin_businesses__business_id__blog_posts__blog_post_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `blog_post_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/blog_posts/generate Generate a blog post using AI Operation id: `generate_blog_post_endpoint_api_v1_admin_businesses__business_id__blog_posts_generate_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `Idempotency-Key` | header | string \| null | no | | | `X-Workflow-Bypass` | header | string \| null | no | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): BlogPostGenerateRequest - `prompt` · string · required: User prompt describing the blog post to generate - `provider` · string | null: Optional AI provider override: openai, anthropic, gemini - `new_blog_candidate_id` · string (uuid) | null: A new-blog candidate id (blog_suggestions, scope new_post); its brief drives the post **Responses** - `200` Successful Response: SuccessResponse_BlogPostGenerateResponse_ - `request_id` · string · required - `success` · true - `data` · BlogPostGenerateResponse · required - `title` · string · required - `slug` · string · required - `excerpt` · string · required - `content_markdown` · string · required - `tags` · string[] - `media_brief` · object: Hero-photo brief for the workflow worker to resolve against the media library. Empty unless blog_media_brief_enabled is on. - `body_images` · object[]: Inline photo briefs, each paired with a [[ks-image:N]] marker in content_markdown. Empty unless blog_body_images_enabled is on. - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] - `brief` · object | null - `metadata` · BlogPostGenerateMetadata · required - `generation_status` · string - `workflow_run_id` · string | null - `thread_id` · string | null - `blog_post` · BlogPostData | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/blog_posts/generate/stream Stream blog generation status events (SSE) Operation id: `stream_blog_generate_api_v1_admin_businesses__business_id__blog_posts_generate_stream_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `workflow_run_id` | query | string | yes | Workflow run id returned by generate endpoint | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Business 6 endpoints. HTML: https://developers.keystone.app/api/console/business/ ### POST /api/v1/admin/businesses Create business Operation id: `create_business_api_v1_admin_businesses_post` Create a business. company_name required; id optional (server generates if omitted). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): BusinessCreateBody - `company_name` · string · required - `year_founded` · integer | null - `website_url` · string | null - `tagline` · string | null - `address` · Address | null - `line1` · string | null - `line2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `country` · string | null - `descriptions` · Descriptions | null - `company_description` · string | null - `about` · string | null - `mission_statement` · string | null - `values` · string[] - `contact_info` · ContactInfo | null - `primary_phone` · string | null - `primary_email` · string | null - `support_email` · string | null - `sales_email` · string | null - `external_management_url` · string | null - `social_profiles` · SocialProfiles | null - `facebook` · string | null - `instagram` · string | null - `twitter` · string | null - `linkedin` · string | null - `youtube` · string | null - `tiktok` · string | null - `google_my_business` · string | null - `pinterest` · string | null - `yelp` · string | null - `tripadvisor` · string | null - `google_review` · string | null - `industries` · BusinessIndustries | null - `primary` · string | null - `secondary` · string[] - `terms_of_service_markdown` · string | null - `privacy_policy_markdown` · string | null - `is_active` · boolean **Responses** - `201` Successful Response: SuccessResponse_BusinessProfileData_ - `request_id` · string · required - `success` · true - `data` · BusinessProfileData · required: Business profile for GET response. - `id` · string · required - `company_name` · string | null - `year_founded` · integer | null - `website_url` · string | null - `tagline` · string | null - `address` · Address | null - `descriptions` · Descriptions | null - `contact_info` · ContactInfo | null - `social_profiles` · SocialProfiles | null - `industries` · BusinessIndustries | null - `terms_of_service_markdown` · string | null - `privacy_policy_markdown` · string | null - `is_active` · boolean - `timezone` · string - `timezone_source` · string - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id} Get business details Operation id: `get_business_api_v1_admin_businesses__business_id__get` Get business by id. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BusinessProfileData_ - `request_id` · string · required - `success` · true - `data` · BusinessProfileData · required: Business profile for GET response. - `id` · string · required - `company_name` · string | null - `year_founded` · integer | null - `website_url` · string | null - `tagline` · string | null - `address` · Address | null - `descriptions` · Descriptions | null - `contact_info` · ContactInfo | null - `social_profiles` · SocialProfiles | null - `industries` · BusinessIndustries | null - `terms_of_service_markdown` · string | null - `privacy_policy_markdown` · string | null - `is_active` · boolean - `timezone` · string - `timezone_source` · string - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id} Update specific fields of business profile Operation id: `patch_business_api_v1_admin_businesses__business_id__patch` Partial update (PATCH). 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): BusinessUpdateBody - `company_name` · string | null - `year_founded` · integer | null - `website_url` · string | null - `tagline` · string | null - `address` · Address | null - `line1` · string | null - `line2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `country` · string | null - `descriptions` · Descriptions | null - `company_description` · string | null - `about` · string | null - `mission_statement` · string | null - `values` · string[] - `contact_info` · ContactInfo | null - `primary_phone` · string | null - `primary_email` · string | null - `support_email` · string | null - `sales_email` · string | null - `external_management_url` · string | null - `social_profiles` · SocialProfiles | null - `facebook` · string | null - `instagram` · string | null - `twitter` · string | null - `linkedin` · string | null - `youtube` · string | null - `tiktok` · string | null - `google_my_business` · string | null - `pinterest` · string | null - `yelp` · string | null - `tripadvisor` · string | null - `google_review` · string | null - `industries` · BusinessIndustries | null - `primary` · string | null - `secondary` · string[] - `terms_of_service_markdown` · string | null - `privacy_policy_markdown` · string | null - `is_active` · boolean | null - `timezone` · string | null **Responses** - `200` Successful Response: SuccessResponse_BusinessProfileData_ - `request_id` · string · required - `success` · true - `data` · BusinessProfileData · required: Business profile for GET response. - `id` · string · required - `company_name` · string | null - `year_founded` · integer | null - `website_url` · string | null - `tagline` · string | null - `address` · Address | null - `descriptions` · Descriptions | null - `contact_info` · ContactInfo | null - `social_profiles` · SocialProfiles | null - `industries` · BusinessIndustries | null - `terms_of_service_markdown` · string | null - `privacy_policy_markdown` · string | null - `is_active` · boolean - `timezone` · string - `timezone_source` · string - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/api-key Get business public API credentials (URL + key) Operation id: `get_business_api_key_api_v1_admin_businesses__business_id__api_key_get` Return the public API URL and active key for a business. 404 if the business doesn't exist. The full key is returned so the console can offer copy-to-clipboard; it is never rendered in full. ``key_prefix``/``api_key`` are null when the business has no active key. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BusinessApiKeyData_ - `request_id` · string · required - `success` · true - `data` · BusinessApiKeyData · required: Public API credentials for a business (admin account overview). ``api_url`` is the global SOR public API base URL (same for every business). ``key_prefix``/``api_key`` are null when the business has no active key yet. ``api_key`` is the full decrypted key, returned so the admin console can offer copy-to-clipboard without ever rendering it in full. - `api_url` · string · required - `key_prefix` · string | null - `api_key` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/dashboard Get dashboard info for a specific business Operation id: `get_business_dashboard_by_id_api_v1_admin_businesses__business_id__dashboard_get` Counts (services, locations, team, faq, jobs, photos) for a specific business. Access control is handled by ``auth_admin("viewer")``: the caller must have a ``business_users`` row for ``business_id``, OR carry ``global_business_access`` (super-admins). Otherwise 403. Returns 404 NOT_FOUND when the business id doesn't exist. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BusinessDashboardData_ - `request_id` · string · required - `success` · true - `data` · BusinessDashboardData · required: Dashboard summary for the authenticated user's business. - `business_id` · string · required - `business_name` · string · required - `services_count` · integer · required - `packages_count` · integer · required - `service_items_count` · integer · required - `locations_count` · integer · required - `team_count` · integer · required - `faq_count` · integer · required - `jobs_count` · integer · required - `photos_count` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/dashboard [Deprecated] Dashboard info for authenticated user's business — use /businesses/{business_id}/dashboard (deprecated) Operation id: `get_business_dashboard_api_v1_admin_businesses_dashboard_get` Get business name and counts (services, locations, team, faq, jobs, photos) for the user's *own* business. Returns 404 NOT_FOUND when the caller has no ``business_users`` row — including for admins who aren't directly attached to any business. Use ``GET /businesses/{business_id}/dashboard`` to fetch counts for a specific business (with the standard business-access auth check). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BusinessDashboardData_ - `request_id` · string · required - `success` · true - `data` · BusinessDashboardData · required: Dashboard summary for the authenticated user's business. - `business_id` · string · required - `business_name` · string · required - `services_count` · integer · required - `packages_count` · integer · required - `service_items_count` · integer · required - `locations_count` · integer · required - `team_count` · integer · required - `faq_count` · integer · required - `jobs_count` · integer · required - `photos_count` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Business claims 3 endpoints. HTML: https://developers.keystone.app/api/console/businesses-claim/ ### GET /api/v1/admin/businesses/{business_id}/claim Who holds this business, and whether the caller can claim or extend Operation id: `get_claim_api_v1_admin_businesses__business_id__claim_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ClaimStateData_ - `request_id` · string · required - `success` · true - `data` · ClaimStateData · required: Everything the console's claim control shows. - `business_id` · string · required - `claim` · ClaimData | null - `launched` · boolean · required - `can_claim` · boolean · required - `can_extend` · boolean · required - `server_time` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/claim Claim a business nobody holds, for 24 hours Operation id: `put_claim_api_v1_admin_businesses__business_id__claim_put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ClaimStateData_ - `request_id` · string · required - `success` · true - `data` · ClaimStateData · required: Everything the console's claim control shows. - `business_id` · string · required - `claim` · ClaimData | null - `launched` · boolean · required - `can_claim` · boolean · required - `can_extend` · boolean · required - `server_time` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/claim/extend Extend the caller's claim once, by another 24 hours Operation id: `extend_claim_api_v1_admin_businesses__business_id__claim_extend_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ClaimStateData_ - `request_id` · string · required - `success` · true - `data` · ClaimStateData · required: Everything the console's claim control shows. - `business_id` · string · required - `claim` · ClaimData | null - `launched` · boolean · required - `can_claim` · boolean · required - `can_extend` · boolean · required - `server_time` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Contacts 45 endpoints. HTML: https://developers.keystone.app/api/console/contacts/ ### GET /api/v1/admin/businesses/{business_id}/contacts List contacts Operation id: `list_contacts_api_v1_admin_businesses__business_id__contacts_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | | | `lifecycle_stage` | query | string \| null | no | | | `signal` | query | string \| null | no | Filter by signal: hot,warm,cool,new,unassigned | | `touch` | query | string \| null | no | Filter by touch: responsive,slow,unresponsive,ghosted,unassigned | | `stage` | query | string \| null | no | Filter by stage: new,engaged,booked,returning,lapsed,lost,unassigned | | `needs_attention` | query | boolean \| null | no | Filter contacts requiring supervisory attention | | `identity_resolution_status` | query | string \| null | no | | | `include_deleted` | query | boolean | no | | | `source` | query | string \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `updated_after` | query | string (date-time) \| null | no | | | `sort` | query | string | no | Sort order. 'last_activity' (default) surfaces the most recent conversation first; also accepts any contact column (created_at, updated_at, ...). | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactData__ - `request_id` · string · required - `success` · true - `data` · ContactData[] · required - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts Create contact Operation id: `create_contact_api_v1_admin_businesses__business_id__contacts_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ContactCreateBody - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `email` · string | null - `phone` · string | null - `emails` · ContactEmailInputBody[] - `email` · string · required - `source` · string | null - `is_primary` · boolean - `phones` · ContactPhoneInputBody[] - `phone` · string · required - `source` · string | null - `is_primary` · boolean - `status` · string - `lifecycle_stage` · string - `metadata_` · object | null - `source` · ContactSourceBody | null - `contact_source_type` · ContactSourceType · required: Allowed contact source values. - `contact_source_detail` · string | null - `external_ref_id` · string | null - `consent` · ContactConsentBody | null - `transactional_sms` · boolean | null - `marketing_sms` · boolean | null - `marketing_email` · boolean | null - `tos_privacy` · boolean | null - `ai_initiate` · boolean **Responses** - `201` Successful Response: SuccessResponse_ContactOutcomeData_ - `request_id` · string · required - `success` · true - `data` · ContactOutcomeData · required: Structured response for POST /contacts and PUT /contacts/upsert. - `contact` · ContactData · required: Contact detail / list item response shape. - `operation` · string · required - `consumer_link_status` · string · required - `duplicate_flagged` · boolean - `identity_review_id` · string | null - `duplicate_review_id` · string | null - `ai_initiation` · AiInitiationResult | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/{contact_id} Get contact detail Operation id: `get_contact_api_v1_admin_businesses__business_id__contacts__contact_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/contacts/{contact_id} Update contact Operation id: `update_contact_api_v1_admin_businesses__business_id__contacts__contact_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ContactUpdateBody - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string | null - `lifecycle_stage` · string | null - `signal` · string | null - `touch` · string | null - `stage` · string | null - `metadata_` · object | null - `last_contacted_at` · string (date-time) | null - `auto_contact_enabled` · boolean | null - `outreach_suppressed` · boolean | null **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/contacts/{contact_id} Delete contact Operation id: `delete_contact_api_v1_admin_businesses__business_id__contacts__contact_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/classification Override contact classification Operation id: `patch_classification_api_v1_admin_businesses__business_id__contacts__contact_id__classification_patch` Override one or more classification dimensions for the contact. Any field omitted in the body is left unchanged. Each changed dimension lands a row in `contact_classification_history` with `changed_by="user:api"` and the supplied `reason`. Manual overrides are NOT sticky — the next AI classification pass (triggered by inbound messages or DELIVERED/READ status events) may overwrite them. To preserve a manual value, take the conversation out of AUTONOMOUS AI mode. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ClassifyBody - `signal` · string | null - `touch` · string | null - `stage` · string | null - `reason` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/classification-history Get classification history Operation id: `get_classification_history_api_v1_admin_businesses__business_id__contacts__contact_id__classification_history_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `field` | query | string \| null | no | Filter by: signal, touch, stage | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ClassificationHistoryItem__ - `request_id` · string · required - `success` · true - `data` · ClassificationHistoryItem[] · required - `type` · string - `field` · string · required - `old_value` · string | null - `new_value` · string · required - `changed_by` · string · required - `reason` · string | null - `created_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/classify Manually classify a contact (legacy POST alias) Operation id: `classify_contact_api_v1_admin_businesses__business_id__contacts__contact_id__classify_post` Override one or more classification dimensions for the contact. Any field omitted in the body is left unchanged. Each changed dimension lands a row in `contact_classification_history` with `changed_by="user:api"` and the supplied `reason`. Manual overrides are NOT sticky — the next AI classification pass (triggered by inbound messages or DELIVERED/READ status events) may overwrite them. To preserve a manual value, take the conversation out of AUTONOMOUS AI mode. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ClassifyBody - `signal` · string | null - `touch` · string | null - `stage` · string | null - `reason` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/conversation Get this contact's conversation Operation id: `get_contact_conversation_api_v1_admin_businesses__business_id__contacts__contact_id__conversation_get` Returns the conversation for this contact's consumer, or `data: null` with 200 when none exists yet. 404 is reserved for the contact itself being missing from this business — the frontend should branch on `data == null`, not on status code. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_Union_ConversationData__NoneType__ - `request_id` · string · required - `success` · true - `data` · ConversationData | null · required - `id` · string · required - `consumer_id` · string | null - `status` · string · required - `assigned_to` · integer | null - `ai_enabled` · boolean - `ai_mode` · string - `last_message_at` · string (date-time) | null - `last_message_preview` · string | null - `last_message_channel` · string | null - `unread_count` · integer - `message_count` · integer - `snoozed_until` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `reply_channel` · ReplyChannelData | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/emails List contact emails Operation id: `list_emails_api_v1_admin_businesses__business_id__contacts__contact_id__emails_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactEmailData__ - `request_id` · string · required - `success` · true - `data` · ContactEmailData[] · required - `id` · string · required - `business_contact_id` · string · required - `business_id` · string · required - `raw_input` · string | null - `email` · string · required - `verified_at` · string (date-time) | null - `source` · string | null - `is_deleted` · boolean - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/emails Add email to contact Operation id: `add_email_api_v1_admin_businesses__business_id__contacts__contact_id__emails_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ContactEmailCreateBody - `email` · string · required - `source` · string | null **Responses** - `201` Successful Response: SuccessResponse_ContactEmailAddResult_ - `request_id` · string · required - `success` · true - `data` · ContactEmailAddResult · required: Response for POST /contacts/{id}/emails. - `email` · ContactEmailData · required: Email record response shape. - `identity_review_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/emails/{email_id} Update contact email Operation id: `update_email_api_v1_admin_businesses__business_id__contacts__contact_id__emails__email_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `email_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ContactEmailPatchBody - `email` · string | null - `source` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactEmailUpdateResult_ - `request_id` · string · required - `success` · true - `data` · ContactEmailUpdateResult · required: Response for PATCH /contacts/{id}/emails/{email_id}. Mirrors `ContactEmailAddResult` so the console can reuse the same response handler — `identity_review_id` is set when a value change on a consumer-linked contact opens (or finds an existing) review. - `email` · ContactEmailData · required: Email record response shape. - `identity_review_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/emails/{email_id} Remove email from contact Operation id: `remove_email_api_v1_admin_businesses__business_id__contacts__contact_id__emails__email_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `email_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/emails/{email_id}/set-primary Set primary email Operation id: `set_primary_email_api_v1_admin_businesses__business_id__contacts__contact_id__emails__email_id__set_primary_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `email_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/insights List contact insights Operation id: `list_contact_insights_api_v1_admin_businesses__business_id__contacts__contact_id__insights_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `include_dismissed` | query | boolean | no | | | `priority` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactInsightData__ - `request_id` · string · required - `success` · true - `data` · ContactInsightData[] · required - `id` · string · required - `contact_id` · string · required - `business_id` · string · required - `insight_type` · string · required - `title` · string · required - `body` · string · required - `priority` · string · required - `action_type` · string | null - `action_data` · object | null - `is_dismissed` · boolean - `expires_at` · string (date-time) | null - `created_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/insights/{insight_id} Get a contact insight Operation id: `get_contact_insight_api_v1_admin_businesses__business_id__contacts__contact_id__insights__insight_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `insight_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactInsightData_ - `request_id` · string · required - `success` · true - `data` · ContactInsightData · required: Contact insight response shape. - `id` · string · required - `contact_id` · string · required - `business_id` · string · required - `insight_type` · string · required - `title` · string · required - `body` · string · required - `priority` · string · required - `action_type` · string | null - `action_data` · object | null - `is_dismissed` · boolean - `expires_at` · string (date-time) | null - `created_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/insights/{insight_id}/dismiss Dismiss a contact insight Operation id: `dismiss_contact_insight_api_v1_admin_businesses__business_id__contacts__contact_id__insights__insight_id__dismiss_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `insight_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactInsightData_ - `request_id` · string · required - `success` · true - `data` · ContactInsightData · required: Contact insight response shape. - `id` · string · required - `contact_id` · string · required - `business_id` · string · required - `insight_type` · string · required - `title` · string · required - `body` · string · required - `priority` · string · required - `action_type` · string | null - `action_data` · object | null - `is_dismissed` · boolean - `expires_at` · string (date-time) | null - `created_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/needs-attention Resolve contact needs-attention overlay Operation id: `resolve_contact_needs_attention_api_v1_admin_businesses__business_id__contacts__contact_id__needs_attention_delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `re_enable_ai` | query | boolean | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: object - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/opt-in Record opt-in Operation id: `opt_in_api_v1_admin_businesses__business_id__contacts__contact_id__opt_in_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): OptInOutBody - `channel` · string · required - `source` · string · required - `occurred_at` · string (date-time) | null - `idempotency_key` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactPreferenceData_ - `request_id` · string · required - `success` · true - `data` · ContactPreferenceData · required: GET /contacts/{id}/preferences response shape. - `id` · string · required - `business_contact_id` · string · required - `transactional_sms_opt_in_at` · string (date-time) | null - `transactional_sms_opt_out_at` · string (date-time) | null - `marketing_sms_opt_in_at` · string (date-time) | null - `marketing_sms_opt_out_at` · string (date-time) | null - `marketing_email_opt_in_at` · string (date-time) | null - `marketing_email_opt_out_at` · string (date-time) | null - `tos_privacy_accepted_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/opt-out Record opt-out Operation id: `opt_out_api_v1_admin_businesses__business_id__contacts__contact_id__opt_out_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): OptInOutBody - `channel` · string · required - `source` · string · required - `occurred_at` · string (date-time) | null - `idempotency_key` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactPreferenceData_ - `request_id` · string · required - `success` · true - `data` · ContactPreferenceData · required: GET /contacts/{id}/preferences response shape. - `id` · string · required - `business_contact_id` · string · required - `transactional_sms_opt_in_at` · string (date-time) | null - `transactional_sms_opt_out_at` · string (date-time) | null - `marketing_sms_opt_in_at` · string (date-time) | null - `marketing_sms_opt_out_at` · string (date-time) | null - `marketing_email_opt_in_at` · string (date-time) | null - `marketing_email_opt_out_at` · string (date-time) | null - `tos_privacy_accepted_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/phones List contact phones Operation id: `list_phones_api_v1_admin_businesses__business_id__contacts__contact_id__phones_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactPhoneData__ - `request_id` · string · required - `success` · true - `data` · ContactPhoneData[] · required - `id` · string · required - `business_contact_id` · string · required - `business_id` · string · required - `raw_input` · string | null - `phone` · string · required - `phone_type` · string | null - `country_code` · string | null - `verified_at` · string (date-time) | null - `source` · string | null - `is_deleted` · boolean - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/phones Add phone to contact Operation id: `add_phone_api_v1_admin_businesses__business_id__contacts__contact_id__phones_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ContactPhoneCreateBody - `phone` · string · required - `source` · string | null **Responses** - `201` Successful Response: SuccessResponse_ContactPhoneAddResult_ - `request_id` · string · required - `success` · true - `data` · ContactPhoneAddResult · required: Response for POST /contacts/{id}/phones. - `phone` · ContactPhoneData · required: Phone record response shape. - `identity_review_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/phones/{phone_id} Update contact phone Operation id: `update_phone_api_v1_admin_businesses__business_id__contacts__contact_id__phones__phone_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `phone_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ContactPhonePatchBody - `phone` · string | null - `phone_type` · string | null - `country_code` · string | null - `source` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactPhoneUpdateResult_ - `request_id` · string · required - `success` · true - `data` · ContactPhoneUpdateResult · required: Response for PATCH /contacts/{id}/phones/{phone_id}. Mirrors `ContactPhoneAddResult` so the console can reuse the same response handler — `identity_review_id` is set when a value change on a consumer-linked contact opens (or finds an existing) review. - `phone` · ContactPhoneData · required: Phone record response shape. - `identity_review_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/phones/{phone_id} Remove phone from contact Operation id: `remove_phone_api_v1_admin_businesses__business_id__contacts__contact_id__phones__phone_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `phone_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/phones/{phone_id}/set-primary Set primary phone Operation id: `set_primary_phone_api_v1_admin_businesses__business_id__contacts__contact_id__phones__phone_id__set_primary_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `phone_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/preferences Get preferences Operation id: `get_preferences_api_v1_admin_businesses__business_id__contacts__contact_id__preferences_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactPreferenceData_ - `request_id` · string · required - `success` · true - `data` · ContactPreferenceData · required: GET /contacts/{id}/preferences response shape. - `id` · string · required - `business_contact_id` · string · required - `transactional_sms_opt_in_at` · string (date-time) | null - `transactional_sms_opt_out_at` · string (date-time) | null - `marketing_sms_opt_in_at` · string (date-time) | null - `marketing_sms_opt_out_at` · string (date-time) | null - `marketing_email_opt_in_at` · string (date-time) | null - `marketing_email_opt_out_at` · string (date-time) | null - `tos_privacy_accepted_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/reassess Trigger a fresh AI classification pass Operation id: `reassess_contact_api_v1_admin_businesses__business_id__contacts__contact_id__reassess_post` Publishes a manual `ClassificationRequest` for this contact's consumer. The classification consumer picks it up out-of-band and writes back signal/touch/stage + a fresh `latest_insight`; expect the new values to land within seconds. Returns the current contact snapshot — poll the same contact-detail endpoint to see the updated classification. Returns 202 even when Kafka is disabled — the next inbound message will pick the contact up; the response includes the current state regardless. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `202` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/score-history Get engagement score history Operation id: `get_score_history_api_v1_admin_businesses__business_id__contacts__contact_id__score_history_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ScoreHistoryItem__ - `request_id` · string · required - `success` · true - `data` · ScoreHistoryItem[] · required - `engagement_score` · integer · required - `purchase_intent` · integer | null - `model_version` · string | null - `reasoning` · string | null - `input_signals` · object | null - `created_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/sources List sources Operation id: `list_sources_api_v1_admin_businesses__business_id__contacts__contact_id__sources_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactSourceData__ - `request_id` · string · required - `success` · true - `data` · ContactSourceData[] · required - `id` · string · required - `business_contact_id` · string · required - `contact_source_type` · ContactSourceType · required: Allowed contact source values. - `contact_source_detail` · string | null - `external_ref_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/{contact_id}/sources Add source Operation id: `add_source_api_v1_admin_businesses__business_id__contacts__contact_id__sources_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ContactSourceCreateBody - `contact_source_type` · ContactSourceType · required: Allowed contact source values. - `contact_source_detail` · string | null - `external_ref_id` · string | null **Responses** - `201` Successful Response: SuccessResponse_ContactSourceData_ - `request_id` · string · required - `success` · true - `data` · ContactSourceData · required: Source attribution response shape. - `id` · string · required - `business_contact_id` · string · required - `contact_source_type` · ContactSourceType · required: Allowed contact source values. - `contact_source_detail` · string | null - `external_ref_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/export Export contacts as CSV Operation id: `export_contacts_api_v1_admin_businesses__business_id__contacts_export_get` Stream every matching contact as a CSV file (one server request, no client-side paging). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `q` | query | string \| null | no | Optional search query; when set, matches the search view | | `status` | query | string \| null | no | | | `lifecycle_stage` | query | string \| null | no | | | `signal` | query | string \| null | no | | | `touch` | query | string \| null | no | | | `stage` | query | string \| null | no | | | `needs_attention` | query | boolean \| null | no | | | `identity_resolution_status` | query | string \| null | no | | | `include_deleted` | query | boolean | no | | | `source` | query | string \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `updated_after` | query | string (date-time) \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/lookup Lookup contact by email/phone Operation id: `lookup_contacts_api_v1_admin_businesses__business_id__contacts_lookup_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ContactLookupBody - `email` · string | null - `phone` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactLookupResult_ - `request_id` · string · required - `success` · true - `data` · ContactLookupResult · required: POST /contacts/lookup response data. - `match_type` · string · required - `contacts` · ContactData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contacts/merge Merge contacts Operation id: `merge_contacts_api_v1_admin_businesses__business_id__contacts_merge_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ContactMergeBody - `surviving_contact_id` · string (uuid) · required - `merged_contact_id` · string (uuid) · required - `field_resolutions` · object | null - `merge_reason` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/needs-attention List contacts needing attention Operation id: `get_needs_attention_api_v1_admin_businesses__business_id__contacts_needs_attention_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_NeedsAttentionData_ - `request_id` · string · required - `success` · true - `data` · NeedsAttentionData · required: GET /contacts/needs-attention response shape. - `items` · NeedsAttentionItem[] · required - `total` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/pipeline-summary Get pipeline summary Operation id: `get_pipeline_summary_api_v1_admin_businesses__business_id__contacts_pipeline_summary_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PipelineSummaryData_ - `request_id` · string · required - `success` · true - `data` · PipelineSummaryData · required: GET /contacts/pipeline-summary response shape. - `summary` · string · required - `stats` · object · required - `generated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/search Search contacts Operation id: `search_contacts_api_v1_admin_businesses__business_id__contacts_search_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `q` | query | string | yes | | | `status` | query | string \| null | no | | | `lifecycle_stage` | query | string \| null | no | | | `signal` | query | string \| null | no | Filter by signal: hot,warm,cool,new,unassigned | | `touch` | query | string \| null | no | Filter by touch: responsive,slow,unresponsive,ghosted,unassigned | | `stage` | query | string \| null | no | Filter by stage: new,engaged,booked,returning,lapsed,lost,unassigned | | `needs_attention` | query | boolean \| null | no | Filter contacts requiring supervisory attention | | `identity_resolution_status` | query | string \| null | no | | | `include_deleted` | query | boolean | no | | | `source` | query | string \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `updated_after` | query | string (date-time) \| null | no | | | `sort` | query | string | no | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactData__ - `request_id` · string · required - `success` · true - `data` · ContactData[] · required - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contacts/stats Get classification stats Operation id: `get_classification_stats_api_v1_admin_businesses__business_id__contacts_stats_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | | | `lifecycle_stage` | query | string \| null | no | | | `signal` | query | string \| null | no | | | `touch` | query | string \| null | no | | | `stage` | query | string \| null | no | | | `needs_attention` | query | boolean \| null | no | | | `identity_resolution_status` | query | string \| null | no | | | `source` | query | string \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `updated_after` | query | string (date-time) \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ClassificationStatsData_ - `request_id` · string · required - `success` · true - `data` · ClassificationStatsData · required: GET /contacts/stats response shape. - `total` · integer · required - `signal` · object · required - `touch` · object · required - `stage` · object · required - `lifecycle_stage` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/contacts/upsert Upsert contact Operation id: `upsert_contact_api_v1_admin_businesses__business_id__contacts_upsert_put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ContactCreateBody - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `email` · string | null - `phone` · string | null - `emails` · ContactEmailInputBody[] - `email` · string · required - `source` · string | null - `is_primary` · boolean - `phones` · ContactPhoneInputBody[] - `phone` · string · required - `source` · string | null - `is_primary` · boolean - `status` · string - `lifecycle_stage` · string - `metadata_` · object | null - `source` · ContactSourceBody | null - `contact_source_type` · ContactSourceType · required: Allowed contact source values. - `contact_source_detail` · string | null - `external_ref_id` · string | null - `consent` · ContactConsentBody | null - `transactional_sms` · boolean | null - `marketing_sms` · boolean | null - `marketing_email` · boolean | null - `tos_privacy` · boolean | null - `ai_initiate` · boolean **Responses** - `200` Successful Response: SuccessResponse_ContactOutcomeData_ - `request_id` · string · required - `success` · true - `data` · ContactOutcomeData · required: Structured response for POST /contacts and PUT /contacts/upsert. - `contact` · ContactData · required: Contact detail / list item response shape. - `operation` · string · required - `consumer_link_status` · string · required - `duplicate_flagged` · boolean - `identity_review_id` · string | null - `duplicate_review_id` · string | null - `ai_initiation` · AiInitiationResult | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/duplicate-reviews List duplicate reviews Operation id: `list_duplicate_reviews_api_v1_admin_businesses__business_id__duplicate_reviews_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_DuplicateReviewData__ - `request_id` · string · required - `success` · true - `data` · DuplicateReviewData[] · required - `id` · string · required - `business_id` · string · required - `contact_id_a` · string · required - `contact_id_b` · string · required - `match_type` · string · required - `confidence_score` · number | null - `status` · string · required - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact_a` · ContactSummary | null - `contact_b` · ContactSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/duplicate-reviews/{review_id} Get duplicate review Operation id: `get_duplicate_review_api_v1_admin_businesses__business_id__duplicate_reviews__review_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_DuplicateReviewData_ - `request_id` · string · required - `success` · true - `data` · DuplicateReviewData · required: Duplicate review response shape. - `id` · string · required - `business_id` · string · required - `contact_id_a` · string · required - `contact_id_b` · string · required - `match_type` · string · required - `confidence_score` · number | null - `status` · string · required - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact_a` · ContactSummary | null - `contact_b` · ContactSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/duplicate-reviews/{review_id}/dismiss Dismiss duplicate pair Operation id: `dismiss_duplicate_pair_api_v1_admin_businesses__business_id__duplicate_reviews__review_id__dismiss_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): DuplicateReviewDismissBody - `reason` · string | null **Responses** - `200` Successful Response: SuccessResponse_DuplicateReviewData_ - `request_id` · string · required - `success` · true - `data` · DuplicateReviewData · required: Duplicate review response shape. - `id` · string · required - `business_id` · string · required - `contact_id_a` · string · required - `contact_id_b` · string · required - `match_type` · string · required - `confidence_score` · number | null - `status` · string · required - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact_a` · ContactSummary | null - `contact_b` · ContactSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/duplicate-reviews/{review_id}/merge Merge duplicate pair Operation id: `merge_duplicate_pair_api_v1_admin_businesses__business_id__duplicate_reviews__review_id__merge_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): DuplicateReviewMergeBody - `surviving_contact_id` · string · required - `field_resolutions` · object | null **Responses** - `200` Successful Response: SuccessResponse_DuplicateReviewData_ - `request_id` · string · required - `success` · true - `data` · DuplicateReviewData · required: Duplicate review response shape. - `id` · string · required - `business_id` · string · required - `contact_id_a` · string · required - `contact_id_b` · string · required - `match_type` · string · required - `confidence_score` · number | null - `status` · string · required - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact_a` · ContactSummary | null - `contact_b` · ContactSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/identity-reviews List identity reviews Operation id: `list_identity_reviews_api_v1_admin_businesses__business_id__identity_reviews_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | | | `business_contact_id` | query | string (uuid) \| null | no | | | `email_matched_consumer_id` | query | string (uuid) \| null | no | | | `phone_matched_consumer_id` | query | string (uuid) \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_IdentityReviewData__ - `request_id` · string · required - `success` · true - `data` · IdentityReviewData[] · required - `id` · string · required - `business_contact_id` · string · required - `email_matched_consumer_id` · string | null - `phone_matched_consumer_id` · string | null - `conflict_snapshot` · object · required - `status` · string · required - `resolved_consumer_id` · string | null - `resolution_action` · string | null - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `notes` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact` · ContactSummary | null - `email_matched_consumer` · ConsumerSummary | null - `phone_matched_consumer` · ConsumerSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/identity-reviews/{review_id} Get identity review Operation id: `get_identity_review_api_v1_admin_businesses__business_id__identity_reviews__review_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_IdentityReviewData_ - `request_id` · string · required - `success` · true - `data` · IdentityReviewData · required: Identity review response shape. - `id` · string · required - `business_contact_id` · string · required - `email_matched_consumer_id` · string | null - `phone_matched_consumer_id` · string | null - `conflict_snapshot` · object · required - `status` · string · required - `resolved_consumer_id` · string | null - `resolution_action` · string | null - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `notes` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact` · ContactSummary | null - `email_matched_consumer` · ConsumerSummary | null - `phone_matched_consumer` · ConsumerSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/identity-reviews/{review_id}/resolve Resolve identity review Operation id: `resolve_identity_review_api_v1_admin_businesses__business_id__identity_reviews__review_id__resolve_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): IdentityReviewResolveBody - `action` · "link_to_consumer" | "merge_consumers_and_link" | "create_new_consumer" | "ignore" · required - `target_consumer_id` · string | null - `notes` · string | null **Responses** - `200` Successful Response: SuccessResponse_IdentityReviewData_ - `request_id` · string · required - `success` · true - `data` · IdentityReviewData · required: Identity review response shape. - `id` · string · required - `business_contact_id` · string · required - `email_matched_consumer_id` · string | null - `phone_matched_consumer_id` · string | null - `conflict_snapshot` · object · required - `status` · string · required - `resolved_consumer_id` · string | null - `resolution_action` · string | null - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `notes` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact` · ContactSummary | null - `email_matched_consumer` · ConsumerSummary | null - `phone_matched_consumer` · ConsumerSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Contacts agent 2 endpoints. HTML: https://developers.keystone.app/api/console/contacts-agent/ ### GET /api/v1/businesses/{business_id}/contacts-agent/config Get effective contacts-agent settings for a business Operation id: `get_config_api_v1_businesses__business_id__contacts_agent_config_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SimpleAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · SimpleAgentConfigData · required: Shared shape for contacts / ads / listings agents. - `business_id` · string · required - `enabled` · boolean · required - `instructions` · string - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/businesses/{business_id}/contacts-agent/config Update contacts-agent settings for a business Operation id: `patch_config_api_v1_businesses__business_id__contacts_agent_config_patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): SimpleAgentConfigPatchBody - `enabled` · boolean | null - `instructions` · string | null **Responses** - `200` Successful Response: SuccessResponse_SimpleAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · SimpleAgentConfigData · required: Shared shape for contacts / ads / listings agents. - `business_id` · string · required - `enabled` · boolean · required - `instructions` · string - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Contexts 6 endpoints. HTML: https://developers.keystone.app/api/console/contexts/ ### PUT /api/v1/admin/businesses/{business_id}/contexts/{context_type} Save business context as new version Operation id: `put_business_context_api_v1_admin_businesses__business_id__contexts__context_type__put` Persist a new immutable version for the selected context type. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `context_type` | path | "business" | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): BusinessContextUpdateBody - `content` · string · required - `source_kind` · "manual" | "generated" | "restored" | null - `source_industry_id` · string | null **Responses** - `200` Successful Response: SuccessResponse_BusinessContextCurrentData_ - `request_id` · string · required - `success` · true - `data` · BusinessContextCurrentData · required: Current context payload for a business + context type. - `id` · string · required - `business_id` · string · required - `context_type` · "business" · required - `version_number` · integer · required - `content` · string · required - `source_kind` · "manual" | "generated" | "restored" · required - `source_industry_id` · string | null - `restored_from_version_id` · string | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contexts/{context_type}/current Get current business context Operation id: `get_current_business_context_api_v1_admin_businesses__business_id__contexts__context_type__current_get` Get the latest saved context for the selected business and context type. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `context_type` | path | "business" | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BusinessContextCurrentData_ - `request_id` · string · required - `success` · true - `data` · BusinessContextCurrentData · required: Current context payload for a business + context type. - `id` · string · required - `business_id` · string · required - `context_type` · "business" · required - `version_number` · integer · required - `content` · string · required - `source_kind` · "manual" | "generated" | "restored" · required - `source_industry_id` · string | null - `restored_from_version_id` · string | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contexts/{context_type}/versions List business context versions Operation id: `list_business_context_versions_api_v1_admin_businesses__business_id__contexts__context_type__versions_get` List context versions for the selected business and context type. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `context_type` | path | "business" | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_BusinessContextVersionSummaryData__ - `request_id` · string · required - `success` · true - `data` · BusinessContextVersionSummaryData[] · required - `id` · string · required - `business_id` · string · required - `context_type` · "business" · required - `version_number` · integer · required - `source_kind` · "manual" | "generated" | "restored" · required - `source_industry_id` · string | null - `restored_from_version_id` · string | null - `created_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/contexts/{context_type}/versions/{version_id} Get business context version detail Operation id: `get_business_context_version_api_v1_admin_businesses__business_id__contexts__context_type__versions__version_id__get` Get the saved content for a historical version. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `context_type` | path | "business" | yes | | | `version_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BusinessContextVersionData_ - `request_id` · string · required - `success` · true - `data` · BusinessContextVersionData · required: Full historical context version payload. - `id` · string · required - `business_id` · string · required - `context_type` · "business" · required - `version_number` · integer · required - `source_kind` · "manual" | "generated" | "restored" · required - `source_industry_id` · string | null - `restored_from_version_id` · string | null - `created_at` · integer · required - `content` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contexts/{context_type}/versions/{version_id}/restore Restore business context version Operation id: `restore_business_context_version_api_v1_admin_businesses__business_id__contexts__context_type__versions__version_id__restore_post` Restore a historical version by copying it into a new current version. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `context_type` | path | "business" | yes | | | `version_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BusinessContextCurrentData_ - `request_id` · string · required - `success` · true - `data` · BusinessContextCurrentData · required: Current context payload for a business + context type. - `id` · string · required - `business_id` · string · required - `context_type` · "business" · required - `version_number` · integer · required - `content` · string · required - `source_kind` · "manual" | "generated" | "restored" · required - `source_industry_id` · string | null - `restored_from_version_id` · string | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/contexts/business/generate-outline Generate business context outline Operation id: `generate_business_context_outline_api_v1_admin_businesses__business_id__contexts_business_generate_outline_post` Generate a Business Context draft from current live SOR business data. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BusinessContextGenerateOutlineData_ - `request_id` · string · required - `success` · true - `data` · BusinessContextGenerateOutlineData · required: Draft generated for the Business Context page. - `context_type` · "business" - `content` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Conversations 15 endpoints. HTML: https://developers.keystone.app/api/console/conversations/ ### GET /api/v1/admin/conversations List conversations Operation id: `list_conversations_api_v1_admin_conversations_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | query | string (uuid) \| null | no | Scope contact hydration to this business. When provided, each list item's `contact` field is the business_contact record for that business; without it, contact is picked from any business the consumer is known to (first match). | | `status` | query | string \| null | no | OPEN \| CLOSED \| SNOOZED | | `assigned_to` | query | integer \| null | no | | | `unread_only` | query | boolean | no | | | `needs_attention` | query | boolean \| null | no | Filter to conversations whose business contact is flagged needs-attention. Requires `business_id` — contacts are per-business while consumers are global, so an unscoped filter would match contacts flagged in other businesses. | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ConversationListItem__ - `request_id` · string · required - `success` · true - `data` · ConversationListItem[] · required - `id` · string · required - `consumer_id` · string | null - `status` · string · required - `assigned_to` · integer | null - `ai_enabled` · boolean - `ai_mode` · string - `last_message_at` · string (date-time) | null - `last_message_preview` · string | null - `last_message_channel` · string | null - `unread_count` · integer - `message_count` · integer - `snoozed_until` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `reply_channel` · ReplyChannelData | null - `contact` · ConversationContactSummary | null - `businesses` · ConversationBusinessSummary[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/conversations Create or fetch conversation for a contact (idempotent) Operation id: `create_conversation_api_v1_admin_conversations_post` Start a conversation with a contact from the console. Returns 200 with the existing conversation when one already exists for the contact's consumer (idempotent). Passing `initial_message` sends the first outbound atomically. Console-created conversations default to `ai_enabled=True`, `ai_mode='AUTONOMOUS'` — the bot drives the thread out of the box. Operators opt out via PATCH /conversations/{id} or by flipping the contact-level `auto_contact_enabled` toggle (which cascades here). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ConversationCreateBody - `business_id` · string · required - `contact_id` · string · required - `channel` · string | null: IMESSAGE (only supported channel today) - `initial_message` · MessageSendBody | null - `body` · string · required - `attachments` · MessageAttachment[] - `url` · string · required - `content_type` · string | null - `filename` · string | null - `source_url` · string | null - `gcs` · object | null - `ingest_status` · string | null - `content_type` · string: TEXT \| MEDIA - `idempotency_key` · string | null - `channel` · string | null - `idempotency_key` · string | null **Responses** - `200` Successful Response: SuccessResponse_ConversationCreateResult_ - `request_id` · string · required - `success` · true - `data` · ConversationCreateResult · required: POST /conversations response data. `created` is True on first insert, False when the existing conversation for this consumer was returned idempotently. Frontend uses it to decide whether to trigger an onboarding UI vs. an append. - `conversation` · ConversationData · required: Conversation detail / list item response shape. - `message` · MessageData | null - `created` · boolean · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/conversations/{conversation_id} Get conversation Operation id: `get_conversation_api_v1_admin_conversations__conversation_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `conversation_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ConversationData_ - `request_id` · string · required - `success` · true - `data` · ConversationData · required: Conversation detail / list item response shape. - `id` · string · required - `consumer_id` · string | null - `status` · string · required - `assigned_to` · integer | null - `ai_enabled` · boolean - `ai_mode` · string - `last_message_at` · string (date-time) | null - `last_message_preview` · string | null - `last_message_channel` · string | null - `unread_count` · integer - `message_count` · integer - `snoozed_until` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `reply_channel` · ReplyChannelData | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/conversations/{conversation_id} Update conversation Operation id: `update_conversation_api_v1_admin_conversations__conversation_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `conversation_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ConversationUpdateBody - `status` · string | null: OPEN \| CLOSED \| SNOOZED - `assigned_to` · integer | null - `ai_enabled` · boolean | null - `ai_mode` · string | null: AUTONOMOUS \| ASSIST \| OFF - `snoozed_until` · string (date-time) | null **Responses** - `200` Successful Response: SuccessResponse_ConversationData_ - `request_id` · string · required - `success` · true - `data` · ConversationData · required: Conversation detail / list item response shape. - `id` · string · required - `consumer_id` · string | null - `status` · string · required - `assigned_to` · integer | null - `ai_enabled` · boolean - `ai_mode` · string - `last_message_at` · string (date-time) | null - `last_message_preview` · string | null - `last_message_channel` · string | null - `unread_count` · integer - `message_count` · integer - `snoozed_until` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `reply_channel` · ReplyChannelData | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/conversations/{conversation_id}/messages List messages in a conversation Operation id: `list_messages_api_v1_admin_conversations__conversation_id__messages_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `conversation_id` | path | string (uuid) | yes | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_MessageData__ - `request_id` · string · required - `success` · true - `data` · MessageData[] · required - `id` · string · required - `conversation_id` · string · required - `consumer_id` · string · required - `direction` · string · required - `sender_type` · string · required - `sender_id` · string | null - `sender_display_name` · string | null - `channel` · string · required - `content_type` · string · required - `body` · string | null - `attachments` · (FormSubmissionAttachment | MessageAttachment)[] - `external_id` · string | null - `status` · string · required - `error_code` · string | null - `error_message` · string | null - `ai_metadata` · object - `created_at` · string (date-time) · required - `delivered_at` · string (date-time) | null - `read_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/conversations/{conversation_id}/messages Send outbound message Operation id: `send_message_api_v1_admin_conversations__conversation_id__messages_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `conversation_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): MessageSendBody - `body` · string · required - `attachments` · MessageAttachment[] - `url` · string · required - `content_type` · string | null - `filename` · string | null - `source_url` · string | null - `gcs` · object | null - `ingest_status` · string | null - `content_type` · string: TEXT \| MEDIA - `idempotency_key` · string | null - `channel` · string | null **Responses** - `201` Successful Response: SuccessResponse_MessageData_ - `request_id` · string · required - `success` · true - `data` · MessageData · required: Message detail / list item response shape. - `id` · string · required - `conversation_id` · string · required - `consumer_id` · string · required - `direction` · string · required - `sender_type` · string · required - `sender_id` · string | null - `sender_display_name` · string | null - `channel` · string · required - `content_type` · string · required - `body` · string | null - `attachments` · (FormSubmissionAttachment | MessageAttachment)[] - `external_id` · string | null - `status` · string · required - `error_code` · string | null - `error_message` · string | null - `ai_metadata` · object - `created_at` · string (date-time) · required - `delivered_at` · string (date-time) | null - `read_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/conversations/{conversation_id}/messages/{message_id} Delete a DRAFT message (ASSIST-mode reject) Operation id: `delete_message_api_v1_admin_conversations__conversation_id__messages__message_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `conversation_id` | path | string (uuid) | yes | | | `message_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/conversations/{conversation_id}/messages/{message_id}/retry Retry a FAILED or PENDING outbound message Operation id: `retry_message_api_v1_admin_conversations__conversation_id__messages__message_id__retry_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `conversation_id` | path | string (uuid) | yes | | | `message_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_MessageData_ - `request_id` · string · required - `success` · true - `data` · MessageData · required: Message detail / list item response shape. - `id` · string · required - `conversation_id` · string · required - `consumer_id` · string · required - `direction` · string · required - `sender_type` · string · required - `sender_id` · string | null - `sender_display_name` · string | null - `channel` · string · required - `content_type` · string · required - `body` · string | null - `attachments` · (FormSubmissionAttachment | MessageAttachment)[] - `external_id` · string | null - `status` · string · required - `error_code` · string | null - `error_message` · string | null - `ai_metadata` · object - `created_at` · string (date-time) · required - `delivered_at` · string (date-time) | null - `read_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/conversations/{conversation_id}/messages/{message_id}/send Approve and send a DRAFT (e.g. AI-authored) message Operation id: `send_draft_api_v1_admin_conversations__conversation_id__messages__message_id__send_post` Used by the ASSIST-mode approve flow: promotes a DRAFT outbound row to PENDING, stamps ai_metadata.review_status='approved' with the approver's user_id, and publishes to raven. Optional `edited_body` in the body allows inline edits before approval. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `conversation_id` | path | string (uuid) | yes | | | `message_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, optional): DraftSendBody | null - `edited_body` · string | null **Responses** - `200` Successful Response: SuccessResponse_MessageData_ - `request_id` · string · required - `success` · true - `data` · MessageData · required: Message detail / list item response shape. - `id` · string · required - `conversation_id` · string · required - `consumer_id` · string · required - `direction` · string · required - `sender_type` · string · required - `sender_id` · string | null - `sender_display_name` · string | null - `channel` · string · required - `content_type` · string · required - `body` · string | null - `attachments` · (FormSubmissionAttachment | MessageAttachment)[] - `external_id` · string | null - `status` · string · required - `error_code` · string | null - `error_message` · string | null - `ai_metadata` · object - `created_at` · string (date-time) · required - `delivered_at` · string (date-time) | null - `read_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/conversations/{conversation_id}/read Mark conversation as read (zero unread_count) Operation id: `mark_conversation_read_api_v1_admin_conversations__conversation_id__read_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `conversation_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ConversationData_ - `request_id` · string · required - `success` · true - `data` · ConversationData · required: Conversation detail / list item response shape. - `id` · string · required - `consumer_id` · string | null - `status` · string · required - `assigned_to` · integer | null - `ai_enabled` · boolean - `ai_mode` · string - `last_message_at` · string (date-time) | null - `last_message_preview` · string | null - `last_message_channel` · string | null - `unread_count` · integer - `message_count` · integer - `snoozed_until` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `reply_channel` · ReplyChannelData | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/conversations/configs List all conversations config versions (newest first) Operation id: `list_conversations_configs_api_v1_admin_conversations_configs_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ConversationsConfigData__ - `request_id` · string · required - `success` · true - `data` · ConversationsConfigData[] · required - `id` · string · required - `version` · integer · required - `is_active` · boolean · required - `persona_system_prompt` · string · required - `prompt_version` · string · required - `classification_provider` · string · required - `conversational_provider` · string · required - `summarization_provider` · string · required - `classification_enabled` · boolean · required - `conversational_enabled` · boolean · required - `conversational_multimodal_enabled` · boolean - `min_autonomous_confidence` · number · required - `forbidden_topics` · string[] - `escalation_keywords` · string[] - `max_auto_replies_per_hour_per_consumer` · integer · required - `max_auto_replies_per_day_global` · integer · required - `summary_trigger_message_count` · integer · required - `verbatim_tail_size` · integer · required - `summary_max_sentences` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `created_by` · string (uuid) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/conversations/configs Create a new conversations config version Operation id: `create_conversations_config_version_api_v1_admin_conversations_configs_post` Inserts a new version that supersedes the current active row. Any field omitted in the body is inherited from the current active version, so callers can submit a small diff. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ConversationsConfigCreateBody - `persona_system_prompt` · string | null - `prompt_version` · string | null - `classification_provider` · string | null - `conversational_provider` · string | null - `summarization_provider` · string | null - `classification_enabled` · boolean | null - `conversational_enabled` · boolean | null - `conversational_multimodal_enabled` · boolean | null - `min_autonomous_confidence` · number | null - `forbidden_topics` · string[] | null - `escalation_keywords` · string[] | null - `max_auto_replies_per_hour_per_consumer` · integer | null - `max_auto_replies_per_day_global` · integer | null - `summary_trigger_message_count` · integer | null - `verbatim_tail_size` · integer | null - `summary_max_sentences` · integer | null **Responses** - `201` Successful Response: SuccessResponse_ConversationsConfigData_ - `request_id` · string · required - `success` · true - `data` · ConversationsConfigData · required: Response shape for /admin/conversations/configs and friends. - `id` · string · required - `version` · integer · required - `is_active` · boolean · required - `persona_system_prompt` · string · required - `prompt_version` · string · required - `classification_provider` · string · required - `conversational_provider` · string · required - `summarization_provider` · string · required - `classification_enabled` · boolean · required - `conversational_enabled` · boolean · required - `conversational_multimodal_enabled` · boolean - `min_autonomous_confidence` · number · required - `forbidden_topics` · string[] - `escalation_keywords` · string[] - `max_auto_replies_per_hour_per_consumer` · integer · required - `max_auto_replies_per_day_global` · integer · required - `summary_trigger_message_count` · integer · required - `verbatim_tail_size` · integer · required - `summary_max_sentences` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `created_by` · string (uuid) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/conversations/configs/{version} Get a conversations config by version Operation id: `get_conversations_config_by_version_api_v1_admin_conversations_configs__version__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `version` | path | integer | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ConversationsConfigData_ - `request_id` · string · required - `success` · true - `data` · ConversationsConfigData · required: Response shape for /admin/conversations/configs and friends. - `id` · string · required - `version` · integer · required - `is_active` · boolean · required - `persona_system_prompt` · string · required - `prompt_version` · string · required - `classification_provider` · string · required - `conversational_provider` · string · required - `summarization_provider` · string · required - `classification_enabled` · boolean · required - `conversational_enabled` · boolean · required - `conversational_multimodal_enabled` · boolean - `min_autonomous_confidence` · number · required - `forbidden_topics` · string[] - `escalation_keywords` · string[] - `max_auto_replies_per_hour_per_consumer` · integer · required - `max_auto_replies_per_day_global` · integer · required - `summary_trigger_message_count` · integer · required - `verbatim_tail_size` · integer · required - `summary_max_sentences` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `created_by` · string (uuid) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/conversations/configs/active Get the active conversations config Operation id: `get_active_conversations_config_api_v1_admin_conversations_configs_active_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ConversationsConfigData_ - `request_id` · string · required - `success` · true - `data` · ConversationsConfigData · required: Response shape for /admin/conversations/configs and friends. - `id` · string · required - `version` · integer · required - `is_active` · boolean · required - `persona_system_prompt` · string · required - `prompt_version` · string · required - `classification_provider` · string · required - `conversational_provider` · string · required - `summarization_provider` · string · required - `classification_enabled` · boolean · required - `conversational_enabled` · boolean · required - `conversational_multimodal_enabled` · boolean - `min_autonomous_confidence` · number · required - `forbidden_topics` · string[] - `escalation_keywords` · string[] - `max_auto_replies_per_hour_per_consumer` · integer · required - `max_auto_replies_per_day_global` · integer · required - `summary_trigger_message_count` · integer · required - `verbatim_tail_size` · integer · required - `summary_max_sentences` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `created_by` · string (uuid) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/global-conversations List conversations across all businesses (admin) Operation id: `list_global_conversations_api_v1_admin_global_conversations_get` Admin cross-business inbox. Always unscoped. `needs_attention` matches conversations where any linked business_contact is flagged. Each row includes `businesses` (id + name) for UI badges and `contact.business_id` for actions. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `status` | query | string \| null | no | OPEN \| CLOSED \| SNOOZED | | `assigned_to` | query | integer \| null | no | | | `unread_only` | query | boolean | no | | | `needs_attention` | query | boolean \| null | no | Filter to conversations where any linked business_contact is flagged needs-attention (cross-business). | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ConversationListItem__ - `request_id` · string · required - `success` · true - `data` · ConversationListItem[] · required - `id` · string · required - `consumer_id` · string | null - `status` · string · required - `assigned_to` · integer | null - `ai_enabled` · boolean - `ai_mode` · string - `last_message_at` · string (date-time) | null - `last_message_preview` · string | null - `last_message_channel` · string | null - `unread_count` · integer - `message_count` · integer - `snoozed_until` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `reply_channel` · ReplyChannelData | null - `contact` · ConversationContactSummary | null - `businesses` · ConversationBusinessSummary[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Conversations agent 2 endpoints. HTML: https://developers.keystone.app/api/console/conversations-agent/ ### GET /api/v1/businesses/{business_id}/conversations-agent/config Get effective conversations-agent settings for a business Operation id: `get_config_api_v1_businesses__business_id__conversations_agent_config_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ConversationsAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · ConversationsAgentConfigData · required: Effective conversations-agent settings for a business. - `business_id` · string · required - `enabled` · boolean · required - `instructions` · string - `website_lead_follow_up_enabled` · boolean - `fb_native_form_follow_up_enabled` · boolean - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/businesses/{business_id}/conversations-agent/config Update conversations-agent settings for a business Operation id: `patch_config_api_v1_businesses__business_id__conversations_agent_config_patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ConversationsAgentConfigPatchBody - `enabled` · boolean | null - `instructions` · string | null - `website_lead_follow_up_enabled` · boolean | null - `fb_native_form_follow_up_enabled` · boolean | null **Responses** - `200` Successful Response: SuccessResponse_ConversationsAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · ConversationsAgentConfigData · required: Effective conversations-agent settings for a business. - `business_id` · string · required - `enabled` · boolean · required - `instructions` · string - `website_lead_follow_up_enabled` · boolean - `fb_native_form_follow_up_enabled` · boolean - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Current user 1 endpoints. HTML: https://developers.keystone.app/api/console/current-user/ ### GET /api/v1/admin/current_user Current authenticated user profile Operation id: `get_current_user_api_v1_admin_current_user_get` Returns the JWT's `user_id` and `role_id` augmented with the display name + email fetched from Heimdal `/users/current_user`. Side effect: refreshes the Redis Heimdal-user cache so message list endpoints can render this user's name on outbound bubbles without a cross-service round-trip. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CurrentUserData_ - `request_id` · string · required - `success` · true - `data` · CurrentUserData · required: Profile of the currently authenticated keystone user. Sourced from Heimdal `/users/current_user`; sor-service caches the relevant bits in Redis (see `services/keystone_user_cache.py`) so the message list can resolve `sender_display_name` without a cross-service round-trip. - `user_id` · string · required - `role_id` · integer · required - `first_name` · string | null - `last_name` · string | null - `email` · string | null - `display_name` · string | null - `features` · CurrentUserFeatures · required: Runtime product access resolved by SOR for the authenticated user. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: FAQ 6 endpoints. HTML: https://developers.keystone.app/api/console/faq/ ### GET /api/v1/admin/businesses/{business_id}/faqs Get all FAQs Operation id: `list_faqs_api_v1_admin_businesses__business_id__faqs_get` Get all FAQs for a business (ordered by display_order). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_FaqData__ - `request_id` · string · required - `success` · true - `data` · FaqData[] · required - `id` · string · required - `business_id` · string · required - `question` · string · required - `answer` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/faqs Create FAQ Operation id: `create_faq_api_v1_admin_businesses__business_id__faqs_post` Create a new FAQ. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): FaqCreateBody - `question` · string · required - `answer` · string · required - `photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_FaqData_ - `request_id` · string · required - `success` · true - `data` · FaqData · required: FAQ for list/GET response. - `id` · string · required - `business_id` · string · required - `question` · string · required - `answer` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/faqs/{faq_id} Get FAQ details Operation id: `get_faq_api_v1_admin_businesses__business_id__faqs__faq_id__get` Get FAQ by id. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `faq_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_FaqData_ - `request_id` · string · required - `success` · true - `data` · FaqData · required: FAQ for list/GET response. - `id` · string · required - `business_id` · string · required - `question` · string · required - `answer` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/faqs/{faq_id} Update FAQ Operation id: `update_faq_api_v1_admin_businesses__business_id__faqs__faq_id__put` Update FAQ details. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `faq_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): FaqUpdateBody - `question` · string | null - `answer` · string | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_FaqData_ - `request_id` · string · required - `success` · true - `data` · FaqData · required: FAQ for list/GET response. - `id` · string · required - `business_id` · string · required - `question` · string · required - `answer` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/faqs/{faq_id} Delete FAQ Operation id: `delete_faq_api_v1_admin_businesses__business_id__faqs__faq_id__delete` Delete a FAQ. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `faq_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/faqs/reorder Reorder FAQs Operation id: `reorder_faqs_api_v1_admin_businesses__business_id__faqs_reorder_put` Reorder FAQs by ordered list of FAQ ids. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): FaqReorderBody - `ordered_faq_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_list_FaqData__ - `request_id` · string · required - `success` · true - `data` · FaqData[] · required - `id` · string · required - `business_id` · string · required - `question` · string · required - `answer` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Forms 8 endpoints. HTML: https://developers.keystone.app/api/console/forms/ ### GET /api/v1/admin/businesses/{business_id}/form_submissions List all form submissions Operation id: `list_submissions_api_v1_admin_businesses__business_id__form_submissions_get` List all form submissions; cursor is the last ``id`` from the previous page (UUID v7). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `form_type` | query | string \| null | no | Filter by form identity (built-in or custom slug, e.g. lead, demo-request) | | `limit` | query | integer | no | | | `cursor` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_FormSubmissionSummary__ - `request_id` · string · required - `success` · true - `data` · FormSubmissionSummary[] · required - `id` · string · required - `form_type` · string · required - `data` · FormSubmissionDataSchema: Submitted form data - arbitrary key/value pairs matching the form's fields definition. Common fields may include: - Lead forms: firstName, lastName, email, phone, message - Consent: transactional_sms_consent, marketing_sms_consent, tos_privacy_consent - Job applications: coverLetter, jobSlug, jobId - Any custom fields defined in the form's fields array - `form` · FormSummary | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/form_submissions/{submission_id} Get form submission details Operation id: `get_submission_api_v1_admin_businesses__business_id__form_submissions__submission_id__get` Get detailed form submission including form info. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `submission_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_FormSubmissionDetail_ - `request_id` · string · required - `success` · true - `data` · FormSubmissionDetail · required: Full form submission data. - `id` · string · required - `business_id` · string · required - `form_id` · string · required - `form_type` · string · required - `data` · FormSubmissionDataSchema: Submitted form data - arbitrary key/value pairs matching the form's fields definition. Common fields may include: - Lead forms: firstName, lastName, email, phone, message - Consent: transactional_sms_consent, marketing_sms_consent, tos_privacy_consent - Job applications: coverLetter, jobSlug, jobId - Any custom fields defined in the form's fields array - `form` · FormSummary | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/form_submissions/{submission_id} Delete form submission Operation id: `delete_submission_api_v1_admin_businesses__business_id__form_submissions__submission_id__delete` Soft delete a form submission. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `submission_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/forms List forms Operation id: `list_forms_api_v1_admin_businesses__business_id__forms_get` List forms for a business (optionally filtered by status) with submission counts. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | Filter by status: draft, live, archived | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_FormSummary__ - `request_id` · string · required - `success` · true - `data` · FormSummary[] · required - `id` · string · required - `business_id` · string · required - `name` · string · required - `form_type` · string · required - `status` · FormStatus: Lifecycle status of a form. Only ``live`` forms are served publicly. - `is_builtin` · boolean - `field_count` · integer - `submissions_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/forms Create a custom form Operation id: `create_form_api_v1_admin_businesses__business_id__forms_post` Create a custom form for a business. ``form_type`` is the immutable per-business identity; when omitted it is slugified from ``name``. A reserved built-in identity (``lead``, …) is rejected (400 RESERVED_FORM_TYPE); a duplicate identity → 409 FORM_ALREADY_EXISTS. New forms default to ``draft``. Snapshots are lazy: a draft gets no snapshot; the first snapshot is created when the form is created (or later set) ``live``. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): FormCreateRequest - `name` · string · required - `form_type` · string | null - `fields` · (FormFieldSchema | FormFieldSchema[])[] - `settings` · FormSettingsSchema | null - `send_events` · string[] - `show_transactional_sms_consent` · boolean - `show_marketing_sms_consent` · boolean - `show_tos_privacy_consent` · boolean - `tos_privacy_required` · boolean - `sync_contact` · boolean - `sync_job` · boolean - `enroll_conversation_ai` · boolean - `allow_extra_fields` · boolean - `status` · FormStatus: Lifecycle status of a form. Only ``live`` forms are served publicly. - `description` · string | null - `submit_button_label` · string | null - `confirmation_message` · string | null **Responses** - `201` Successful Response: SuccessResponse_FormData_ - `request_id` · string · required - `success` · true - `data` · FormData · required: Full form data including fields and settings. - `id` · string · required - `business_id` · string · required - `name` · string · required - `form_type` · string · required - `status` · FormStatus: Lifecycle status of a form. Only ``live`` forms are served publicly. - `is_builtin` · boolean - `fields` · (FormFieldSchema | FormFieldSchema[])[] - `settings` · FormSettingsSchema: Settings for form behavior and consent flags. - `description` · string | null - `submit_button_label` · string | null - `confirmation_message` · string | null - `submissions_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/forms/{form_id} Get form details Operation id: `get_form_api_v1_admin_businesses__business_id__forms__form_id__get` Get detailed form including fields, settings, and submission count. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `form_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_FormData_ - `request_id` · string · required - `success` · true - `data` · FormData · required: Full form data including fields and settings. - `id` · string · required - `business_id` · string · required - `name` · string · required - `form_type` · string · required - `status` · FormStatus: Lifecycle status of a form. Only ``live`` forms are served publicly. - `is_builtin` · boolean - `fields` · (FormFieldSchema | FormFieldSchema[])[] - `settings` · FormSettingsSchema: Settings for form behavior and consent flags. - `description` · string | null - `submit_button_label` · string | null - `confirmation_message` · string | null - `submissions_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/forms/{form_id} Update form Operation id: `update_form_api_v1_admin_businesses__business_id__forms__form_id__put` Update a form's name, fields, and/or settings. Partial update: omitted keys are left unchanged. ``form_type`` is immutable; fields marked ``fixed: true`` cannot be removed, modified, or unmarked unless ``bypass_fixed: true`` is sent — which requires the caller to be a **platform admin** (role_id == 100). Changing a **custom** form's ``settings`` likewise requires platform admin (a no-op settings payload is allowed). 404 if the form does not exist. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `form_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): FormUpdateRequest - `name` · string | null - `fields` · (FormFieldSchema | FormFieldSchema[])[] | null - `settings` · FormSettingsSchema | null - `send_events` · string[] - `show_transactional_sms_consent` · boolean - `show_marketing_sms_consent` · boolean - `show_tos_privacy_consent` · boolean - `tos_privacy_required` · boolean - `sync_contact` · boolean - `sync_job` · boolean - `enroll_conversation_ai` · boolean - `allow_extra_fields` · boolean - `status` · FormStatus | null - `description` · string | null - `submit_button_label` · string | null - `confirmation_message` · string | null - `bypass_fixed` · boolean **Responses** - `200` Successful Response: SuccessResponse_FormData_ - `request_id` · string · required - `success` · true - `data` · FormData · required: Full form data including fields and settings. - `id` · string · required - `business_id` · string · required - `name` · string · required - `form_type` · string · required - `status` · FormStatus: Lifecycle status of a form. Only ``live`` forms are served publicly. - `is_builtin` · boolean - `fields` · (FormFieldSchema | FormFieldSchema[])[] - `settings` · FormSettingsSchema: Settings for form behavior and consent flags. - `description` · string | null - `submit_button_label` · string | null - `confirmation_message` · string | null - `submissions_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/forms/{form_id}/submissions List submissions for a form Operation id: `list_form_submissions_api_v1_admin_businesses__business_id__forms__form_id__submissions_get` List submissions for a specific form; cursor is the last ``id`` from the previous page (UUID v7). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `form_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `cursor` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_FormSubmissionSummary__ - `request_id` · string · required - `success` · true - `data` · FormSubmissionSummary[] · required - `id` · string · required - `form_type` · string · required - `data` · FormSubmissionDataSchema: Submitted form data - arbitrary key/value pairs matching the form's fields definition. Common fields may include: - Lead forms: firstName, lastName, email, phone, message - Consent: transactional_sms_consent, marketing_sms_consent, tos_privacy_consent - Job applications: coverLetter, jobSlug, jobId - Any custom fields defined in the form's fields array - `form` · FormSummary | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Front desk 3 endpoints. HTML: https://developers.keystone.app/api/console/front-desk/ ### GET /api/v1/admin/businesses/{business_id}/front-desk Front Desk status for a business Operation id: `get_front_desk_api_v1_admin_businesses__business_id__front_desk_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_FrontDeskStatusData_ - `request_id` · string · required - `success` · true - `data` · FrontDeskStatusData · required: The Front Desk state for one business, as the settings page shows it. ``enabled`` is derived: true iff the business holds a live Front Desk number (design doc §6.3 — no separate config row in phase 1). ``primary_phone`` is the business's own published number, shown so the owner can set up carrier forwarding from it to ``phone_number``. - `business_id` · string · required - `enabled` · boolean · required - `phone_number` · string | null - `number_status` · string | null - `primary_phone` · string | null - `can_enable` · boolean · required - `blocked_reason` · "no_primary_phone" | "not_configured" | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/front-desk/disable Turn Front Desk off (releases the phone number right away) Operation id: `disable_front_desk_api_v1_admin_businesses__business_id__front_desk_disable_post` Releases the number at Retell and marks it released here — the number is gone for good; turning Front Desk on again buys a fresh one. Idempotent when already off. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_FrontDeskStatusData_ - `request_id` · string · required - `success` · true - `data` · FrontDeskStatusData · required: The Front Desk state for one business, as the settings page shows it. ``enabled`` is derived: true iff the business holds a live Front Desk number (design doc §6.3 — no separate config row in phase 1). ``primary_phone`` is the business's own published number, shown so the owner can set up carrier forwarding from it to ``phone_number``. - `business_id` · string · required - `enabled` · boolean · required - `phone_number` · string | null - `number_status` · string | null - `primary_phone` · string | null - `can_enable` · boolean · required - `blocked_reason` · "no_primary_phone" | "not_configured" | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/front-desk/enable Turn Front Desk on (auto-provisions a phone number) Operation id: `enable_front_desk_api_v1_admin_businesses__business_id__front_desk_enable_post` Buys a US number in the area code of the business's primary phone (same-state, then any-US fallback), binds the shared Front Desk agent, and activates it immediately. Idempotent when already on. 422 when the business has no primary phone; 503 when Retell is not configured. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_FrontDeskStatusData_ - `request_id` · string · required - `success` · true - `data` · FrontDeskStatusData · required: The Front Desk state for one business, as the settings page shows it. ``enabled`` is derived: true iff the business holds a live Front Desk number (design doc §6.3 — no separate config row in phase 1). ``primary_phone`` is the business's own published number, shown so the owner can set up carrier forwarding from it to ``phone_number``. - `business_id` · string · required - `enabled` · boolean · required - `phone_number` · string | null - `number_status` · string | null - `primary_phone` · string | null - `can_enable` · boolean · required - `blocked_reason` · "no_primary_phone" | "not_configured" | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Global agent 2 endpoints. HTML: https://developers.keystone.app/api/console/global-agent/ ### GET /api/v1/businesses/{business_id}/global-agent/config Get effective global agent settings for a business Operation id: `get_config_api_v1_businesses__business_id__global_agent_config_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_GlobalAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · GlobalAgentConfigData · required: Business-wide instructions shared across all agents. - `business_id` · string · required - `instructions` · string - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/businesses/{business_id}/global-agent/config Update global agent settings for a business Operation id: `patch_config_api_v1_businesses__business_id__global_agent_config_patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): GlobalAgentConfigPatchBody - `instructions` · string | null **Responses** - `200` Successful Response: SuccessResponse_GlobalAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · GlobalAgentConfigData · required: Business-wide instructions shared across all agents. - `business_id` · string · required - `instructions` · string - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Import data 19 endpoints. HTML: https://developers.keystone.app/api/console/import-data/ ### GET /api/v1/admin/businesses/{business_id}/import-data/custom-imports List custom import runs Operation id: `list_custom_import_runs_api_v1_admin_businesses__business_id__import_data_custom_imports_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_CustomImportRunData__ - `request_id` · string · required - `success` · true - `data` · CustomImportRunData[] · required - `id` · string · required - `business_id` · string · required - `import_name` · string | null - `status` · "queued" | "running" | "awaiting_review" | "completed" | "failed" · required - `merge_mode` · string · required - `text_content` · string | null - `ingest` · ImportDataStageStatus | null - `extract` · ImportDataStageStatus | null - `merge` · ImportDataStageStatus | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/import-data/custom-imports Create a custom import run Operation id: `create_custom_import_api_v1_admin_businesses__business_id__import_data_custom_imports_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (multipart/form-data, required): Body_create_custom_import_api_v1_admin_businesses__business_id__import_data_custom_imports_post - `import_name` · string | null - `text_content` · string | null - `files` · string[] | null **Responses** - `201` Successful Response: SuccessResponse_CustomImportActionResponseData_ - `request_id` · string · required - `success` · true - `data` · CustomImportActionResponseData · required - `run` · CustomImportRunData · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/custom-imports/{import_run_id} Get a custom import run Operation id: `get_custom_import_run_api_v1_admin_businesses__business_id__import_data_custom_imports__import_run_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `import_run_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CustomImportRunData_ - `request_id` · string · required - `success` · true - `data` · CustomImportRunData · required - `id` · string · required - `business_id` · string · required - `import_name` · string | null - `status` · "queued" | "running" | "awaiting_review" | "completed" | "failed" · required - `merge_mode` · string · required - `text_content` · string | null - `ingest` · ImportDataStageStatus | null - `extract` · ImportDataStageStatus | null - `merge` · ImportDataStageStatus | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/import-data/custom-imports/{import_run_id}/extract Re-run extraction on stored custom import content Operation id: `rerun_custom_import_extraction_api_v1_admin_businesses__business_id__import_data_custom_imports__import_run_id__extract_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `import_run_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ReExtractResponseData_ - `request_id` · string · required - `success` · true - `data` · ReExtractResponseData · required - `run_id` · string · required - `status` · string · required - `attempt_id` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/custom-imports/{import_run_id}/extraction Get the latest custom import extraction payload Operation id: `get_custom_import_extraction_api_v1_admin_businesses__business_id__import_data_custom_imports__import_run_id__extraction_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `import_run_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CustomImportExtractionData_ - `request_id` · string · required - `success` · true - `data` · CustomImportExtractionData · required - `attempt_id` · string · required - `run_id` · string · required - `status` · "queued" | "running" | "completed" | "failed" · required - `extractor_version` · string · required - `started_at` · integer | null - `completed_at` · integer | null - `payload` · ExtractionPayloadData - `summary` · object | null - `error_message` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/custom-imports/{import_run_id}/files List files attached to a custom import run Operation id: `list_custom_import_files_api_v1_admin_businesses__business_id__import_data_custom_imports__import_run_id__files_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `import_run_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `cursor` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_CustomImportFileData__ - `request_id` · string · required - `success` · true - `data` · CustomImportFileData[] · required - `id` · string · required - `original_filename` · string · required - `mime_type` · string | null - `byte_size` · integer | null - `checksum_sha256` · string | null - `storage_bucket` · string | null - `storage_blob_name` · string | null - `storage_object_url` · string | null - `extracted_text` · string | null - `normalized_text` · string | null - `parse_status` · string · required - `parse_error` · string | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/custom-imports/{import_run_id}/files/{file_id}/download Download a stored custom import file Operation id: `download_custom_import_file_api_v1_admin_businesses__business_id__import_data_custom_imports__import_run_id__files__file_id__download_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `import_run_id` | path | string (uuid) | yes | | | `file_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/import-data/custom-imports/{import_run_id}/merge Apply a selected subset of a custom import into the business record Operation id: `apply_custom_import_merge_api_v1_admin_businesses__business_id__import_data_custom_imports__import_run_id__merge_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `import_run_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): MergeSelectionBody - `extract_attempt_id` · string · required - `business_profile` · MergeBusinessProfileSelection | null - `scalars` · string[] - `contact_info` · string[] - `address` · boolean - `descriptions` · string[] - `social_profiles` · string[] - `services` · integer[] - `packages` · integer[] - `service_items` · integer[] - `offers` · integer[] - `jobs` · integer[] - `faqs` · integer[] - `locations` · integer[] - `team_members` · integer[] - `blog_posts` · integer[] - `blog_post_authors` · integer[] - `blog_post_tags` · integer[] - `contexts` · boolean **Responses** - `200` Successful Response: SuccessResponse_MergeApplyResponseData_ - `request_id` · string · required - `success` · true - `data` · MergeApplyResponseData · required - `run_id` · string · required - `status` · string · required - `merge_summary` · MergeSummaryData · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/custom-imports/{import_run_id}/review Get the merged review view for a custom import run Operation id: `get_custom_import_review_api_v1_admin_businesses__business_id__import_data_custom_imports__import_run_id__review_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `import_run_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CustomImportReviewData_ - `request_id` · string · required - `success` · true - `data` · CustomImportReviewData · required - `run` · CustomImportRunData · required - `extraction` · CustomImportExtractionData | null - `merge_summary` · MergeSummaryData | null - `current_business_profile` · ExtractedBusinessProfileData | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/website-scrapes List website scrape runs Operation id: `list_website_scrape_runs_api_v1_admin_businesses__business_id__import_data_website_scrapes_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_WebsiteScrapeRunData__ - `request_id` · string · required - `success` · true - `data` · WebsiteScrapeRunData[] · required - `id` · string · required - `business_id` · string · required - `submitted_url` · string · required - `normalized_domain` · string · required - `status` · "queued" | "running" | "awaiting_review" | "completed" | "failed" · required - `scope_mode` · string · required - `merge_mode` · string · required - `onboarding` · object | null - `rescrape_of` · string | null - `last_heartbeat_at` · integer | null - `crawl` · ImportDataStageStatus | null - `extract` · ImportDataStageStatus | null - `merge` · ImportDataStageStatus | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/import-data/website-scrapes Start a website scrape run Operation id: `start_website_scrape_api_v1_admin_businesses__business_id__import_data_website_scrapes_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): WebsiteScrapeStartBody - `website_url` · string · required - `idempotency_key` · string | null - `rescrape_of` · string | null - `auto_onboard` · boolean - `user_prompt` · string | null **Responses** - `201` Successful Response: SuccessResponse_RunActionResponseData_ - `request_id` · string · required - `success` · true - `data` · RunActionResponseData · required - `run` · WebsiteScrapeRunData · required - `reused_existing_run` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/website-scrapes/{scrape_run_id} Get a website scrape run Operation id: `get_website_scrape_run_api_v1_admin_businesses__business_id__import_data_website_scrapes__scrape_run_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `scrape_run_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteScrapeRunData_ - `request_id` · string · required - `success` · true - `data` · WebsiteScrapeRunData · required - `id` · string · required - `business_id` · string · required - `submitted_url` · string · required - `normalized_domain` · string · required - `status` · "queued" | "running" | "awaiting_review" | "completed" | "failed" · required - `scope_mode` · string · required - `merge_mode` · string · required - `onboarding` · object | null - `rescrape_of` · string | null - `last_heartbeat_at` · integer | null - `crawl` · ImportDataStageStatus | null - `extract` · ImportDataStageStatus | null - `merge` · ImportDataStageStatus | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/website-scrapes/{scrape_run_id}/assets List stored crawl assets for a website scrape Operation id: `list_website_scrape_assets_api_v1_admin_businesses__business_id__import_data_website_scrapes__scrape_run_id__assets_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `scrape_run_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `cursor` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_WebsiteScrapeAssetData__ - `request_id` · string · required - `success` · true - `data` · WebsiteScrapeAssetData[] · required - `id` · string · required - `page_id` · string | null - `asset_url` · string · required - `asset_host` · string | null - `asset_type` · string · required - `mime_type` · string | null - `alt_text` · string | null - `source_page_url` · string | null - `width` · integer | null - `height` · integer | null - `byte_size` · integer | null - `checksum_sha256` · string | null - `download_status` · string · required - `download_error` · string | null - `storage_status` · string · required - `storage_error` · string | null - `storage_provider` · string | null - `storage_bucket` · string | null - `storage_blob_name` · string | null - `storage_object_url` · string | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/import-data/website-scrapes/{scrape_run_id}/extract Re-run extraction on stored website scrape pages Operation id: `rerun_website_scrape_extraction_api_v1_admin_businesses__business_id__import_data_website_scrapes__scrape_run_id__extract_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `scrape_run_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ReExtractResponseData_ - `request_id` · string · required - `success` · true - `data` · ReExtractResponseData · required - `run_id` · string · required - `status` · string · required - `attempt_id` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/website-scrapes/{scrape_run_id}/extraction Get the latest website scrape extraction payload Operation id: `get_website_scrape_extraction_api_v1_admin_businesses__business_id__import_data_website_scrapes__scrape_run_id__extraction_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `scrape_run_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteScrapeExtractionData_ - `request_id` · string · required - `success` · true - `data` · WebsiteScrapeExtractionData · required - `attempt_id` · string · required - `run_id` · string · required - `status` · "queued" | "running" | "completed" | "failed" · required - `extractor_version` · string · required - `started_at` · integer | null - `completed_at` · integer | null - `payload` · ExtractionPayloadData - `summary` · object | null - `error_message` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/import-data/website-scrapes/{scrape_run_id}/merge Apply a selected subset of extracted data into the business record Operation id: `apply_website_scrape_merge_api_v1_admin_businesses__business_id__import_data_website_scrapes__scrape_run_id__merge_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `scrape_run_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): MergeSelectionBody - `extract_attempt_id` · string · required - `business_profile` · MergeBusinessProfileSelection | null - `scalars` · string[] - `contact_info` · string[] - `address` · boolean - `descriptions` · string[] - `social_profiles` · string[] - `services` · integer[] - `packages` · integer[] - `service_items` · integer[] - `offers` · integer[] - `jobs` · integer[] - `faqs` · integer[] - `locations` · integer[] - `team_members` · integer[] - `blog_posts` · integer[] - `blog_post_authors` · integer[] - `blog_post_tags` · integer[] - `contexts` · boolean **Responses** - `200` Successful Response: SuccessResponse_MergeApplyResponseData_ - `request_id` · string · required - `success` · true - `data` · MergeApplyResponseData · required - `run_id` · string · required - `status` · string · required - `merge_summary` · MergeSummaryData · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/website-scrapes/{scrape_run_id}/pages List stored crawl pages for a website scrape Operation id: `list_website_scrape_pages_api_v1_admin_businesses__business_id__import_data_website_scrapes__scrape_run_id__pages_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `scrape_run_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `cursor` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_WebsiteScrapePageData__ - `request_id` · string · required - `success` · true - `data` · WebsiteScrapePageData[] · required - `id` · string · required - `url` · string · required - `normalized_url` · string · required - `title` · string | null - `http_status` · integer | null - `content_type` · string | null - `depth` · integer · required - `markdown` · string | null - `cleaned_html` · string | null - `raw_text` · string | null - `metadata_json` · object | null - `links_json` · object | null - `images_json` · object[] | null - `crawl_error` · string | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/website-scrapes/{scrape_run_id}/pages/{page_id} Get a stored crawl page Operation id: `get_website_scrape_page_api_v1_admin_businesses__business_id__import_data_website_scrapes__scrape_run_id__pages__page_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `scrape_run_id` | path | string (uuid) | yes | | | `page_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteScrapePageData_ - `request_id` · string · required - `success` · true - `data` · WebsiteScrapePageData · required - `id` · string · required - `url` · string · required - `normalized_url` · string · required - `title` · string | null - `http_status` · integer | null - `content_type` · string | null - `depth` · integer · required - `markdown` · string | null - `cleaned_html` · string | null - `raw_text` · string | null - `metadata_json` · object | null - `links_json` · object | null - `images_json` · object[] | null - `crawl_error` · string | null - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/import-data/website-scrapes/{scrape_run_id}/review Get the merged review view for a website scrape Operation id: `get_website_scrape_review_api_v1_admin_businesses__business_id__import_data_website_scrapes__scrape_run_id__review_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `scrape_run_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteScrapeReviewData_ - `request_id` · string · required - `success` · true - `data` · WebsiteScrapeReviewData · required - `run` · WebsiteScrapeRunData · required - `extraction` · WebsiteScrapeExtractionData | null - `merge_summary` · MergeSummaryData | null - `current_business_profile` · ExtractedBusinessProfileData | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Industry 6 endpoints. HTML: https://developers.keystone.app/api/console/industry/ ### GET /api/v1/admin/businesses/{business_id}/industries Get business's primary and secondary industry Operation id: `get_business_industries_api_v1_admin_businesses__business_id__industries_get` Get business's primary and secondary industry (resolved to full industry data). 404 if business not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BusinessIndustriesData_ - `request_id` · string · required - `success` · true - `data` · BusinessIndustriesData · required: Business's primary and secondary industry (resolved to full industry data). - `primary` · IndustryData | null - `secondary` · IndustryData[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/industries Update primary and/or secondary industry of business Operation id: `patch_business_industries_api_v1_admin_businesses__business_id__industries_patch` Update primary and/or secondary industry. 404 if business not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): BusinessIndustriesPatchBody - `primary` · string | null - `secondary` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_BusinessIndustriesData_ - `request_id` · string · required - `success` · true - `data` · BusinessIndustriesData · required: Business's primary and secondary industry (resolved to full industry data). - `primary` · IndustryData | null - `secondary` · IndustryData[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/industries List all industries Operation id: `list_industries_api_v1_admin_industries_get` Get list of all industries (ordered by name). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_IndustryData__ - `request_id` · string · required - `success` · true - `data` · IndustryData[] · required - `id` · string · required - `industry_name` · string · required - `industry_description` · string | null - `industry_short_description` · string | null - `accounts_count` · integer - `stock_photos_count` · integer - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/industries Create industry Operation id: `create_industry_api_v1_admin_industries_post` Create an industry with client-provided id. Returns 201 on create; 409 when the industry id already exists. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): IndustryCreateBody - `id` · string | null - `name` · string · required - `description` · string · required **Responses** - `201` Successful Response: SuccessResponse_IndustryData_ - `request_id` · string · required - `success` · true - `data` · IndustryData · required: Industry for list/GET response. - `id` · string · required - `industry_name` · string · required - `industry_description` · string | null - `industry_short_description` · string | null - `accounts_count` · integer - `stock_photos_count` · integer - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/industries/{industry_id} Get industry by industry ID Operation id: `get_industry_api_v1_admin_industries__industry_id__get` Get one industry by id. 404 if missing. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_IndustryDetailsData_ - `request_id` · string · required - `success` · true - `data` · IndustryDetailsData · required: Industry for details (GET by id) response. - `id` · string · required - `industry_name` · string · required - `industry_description` · string | null - `industry_short_description` · string | null - `accounts_count` · integer - `stock_photos_count` · integer - `updated_at` · integer · required - `created_at` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/industries/{industry_id} Update industry details Operation id: `patch_industry_api_v1_admin_industries__industry_id__patch` Partially update industry name and descriptions. 404 if industry not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): IndustryPatchBody - `industry_name` · string | null - `industry_description` · string | null - `industry_short_description` · string | null **Responses** - `200` Successful Response: SuccessResponse_IndustryData_ - `request_id` · string · required - `success` · true - `data` · IndustryData · required: Industry for list/GET response. - `id` · string · required - `industry_name` · string · required - `industry_description` · string | null - `industry_short_description` · string | null - `accounts_count` · integer - `stock_photos_count` · integer - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Integrations status 1 endpoints. HTML: https://developers.keystone.app/api/console/integrations-status/ ### GET /api/v1/admin/businesses/{business_id}/integrations/status Combined status for every integration on the console's rail Operation id: `get_integrations_status_api_v1_admin_businesses__business_id__integrations_status_get` All eight rail rows in one payload, always complete and in rail order. Rows report ``unknown``/``not_configured`` where the signal is missing — never a fabricated ``connected``. The two OAuth-backed rows (Meta, Google Business Profile) separate "authenticated" from "actually usable" and put the blocking reason in ``detail``, so an unaccepted GBP location-manager invite reads as ``pending`` with the reason rather than as a green tick. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_IntegrationsStatusData_ - `request_id` · string · required - `success` · true - `data` · IntegrationsStatusData · required: The whole rail in one payload — the point of the endpoint. ``rows`` is ordered as the console renders it (Deployment, GitHub, Cloudflare, Meta, Google Business Profile, PostHog, Google Tag Manager, Google Search Console) and always carries every row, so the console never has to reason about which calls succeeded. - `business_id` · string (uuid) · required - `website_id` · string (uuid) | null - `rows` · IntegrationStatusRow[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Jobs 7 endpoints. HTML: https://developers.keystone.app/api/console/jobs/ ### GET /api/v1/admin/businesses/{business_id}/job_postings Get all job postings Operation id: `list_job_postings_api_v1_admin_businesses__business_id__job_postings_get` Get all job postings for a business (ordered by display_order, then posted_at). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_JobPostingData__ - `request_id` · string · required - `success` · true - `data` · JobPostingData[] · required - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus · required: Job posting status enum. - `featured` · boolean - `display_order` · integer - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] - `photos` · PhotoData[] - `applications_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/job_postings Create job posting Operation id: `create_job_posting_api_v1_admin_businesses__business_id__job_postings_post` Create a new job posting. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): JobPostingCreateBody - `title` · string · required - `slug` · string | null - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus: Job posting status enum. - `featured` · boolean - `display_order` · integer | null - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_JobPostingData_ - `request_id` · string · required - `success` · true - `data` · JobPostingData · required: Job posting for admin list/GET response. - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus · required: Job posting status enum. - `featured` · boolean - `display_order` · integer - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] - `photos` · PhotoData[] - `applications_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/job_postings/{job_posting_id} Get job posting details Operation id: `get_job_posting_api_v1_admin_businesses__business_id__job_postings__job_posting_id__get` Get job posting by id. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `job_posting_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_JobPostingData_ - `request_id` · string · required - `success` · true - `data` · JobPostingData · required: Job posting for admin list/GET response. - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus · required: Job posting status enum. - `featured` · boolean - `display_order` · integer - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] - `photos` · PhotoData[] - `applications_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/job_postings/{job_posting_id} Update job posting Operation id: `update_job_posting_api_v1_admin_businesses__business_id__job_postings__job_posting_id__put` Update job posting details. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `job_posting_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): JobPostingUpdateBody - `title` · string | null - `slug` · string | null - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType | null - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus | null - `featured` · boolean | null - `display_order` · integer | null - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_JobPostingData_ - `request_id` · string · required - `success` · true - `data` · JobPostingData · required: Job posting for admin list/GET response. - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus · required: Job posting status enum. - `featured` · boolean - `display_order` · integer - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] - `photos` · PhotoData[] - `applications_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/job_postings/{job_posting_id} Delete job posting Operation id: `delete_job_posting_api_v1_admin_businesses__business_id__job_postings__job_posting_id__delete` Soft delete a job posting. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `job_posting_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/job_postings/{job_posting_id}/applications List job applications for a posting Operation id: `list_job_applications_api_v1_admin_businesses__business_id__job_postings__job_posting_id__applications_get` List job applications for a specific job posting. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `job_posting_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `cursor` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_JobApplicationData__ - `request_id` · string · required - `success` · true - `data` · JobApplicationData[] · required - `id` · string · required - `form_type` · string · required - `data` · object · required - `contact` · object | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/job_postings/reorder Reorder job postings Operation id: `reorder_job_postings_api_v1_admin_businesses__business_id__job_postings_reorder_post` Reorder job postings by ordered list of job posting ids. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): JobPostingsReorderBody - `ordered_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_list_JobPostingData__ - `request_id` · string · required - `success` · true - `data` · JobPostingData[] · required - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus · required: Job posting status enum. - `featured` · boolean - `display_order` · integer - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] - `photos` · PhotoData[] - `applications_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Listing accounts 6 endpoints. HTML: https://developers.keystone.app/api/console/listing-accounts/ ### POST /api/v1/admin/businesses/{business_id}/listings/{listing_id}/keystone-invite/retry Retry accepting the Keystone location-manager invite for a listing Operation id: `retry_keystone_invite_api_v1_admin_businesses__business_id__listings__listing_id__keystone_invite_retry_post` Fire the durable accept worker for a listing whose Keystone manager invite is stuck 'pending'. Idempotent and non-blocking — the worker retries acceptance off-request. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/listings/{listing_id}/mapping Map or unmap a listing to an internal SOR location Operation id: `update_listing_mapping_api_v1_admin_businesses__business_id__listings__listing_id__mapping_patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ListingInternalMappingBody - `internal_location_id` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_ListingLocationData_ - `request_id` · string · required - `success` · true - `data` · ListingLocationData · required: One provider-backed listing (``listings`` table); exposed as a *location* in admin APIs. - `id` · string (uuid) · required - `business_id` · string (uuid) · required - `listing_account_id` · string (uuid) · required - `provider` · ListingProvider · required: Listing integration surface (``listing_accounts``, ``listings``, …). Distinct from ``OAuthProvider``. - `provider_listing_id` · string · required - `provider_resource_name` · string · required - `internal_location_id` · string (uuid) | null - `title` · string | null - `store_code` · string | null - `location_metadata` · object | null - `overview` · object | null - `synced_at` · string (date-time) | null - `is_managed` · boolean · required - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/listings/accounts List listing provider accounts Operation id: `list_listing_accounts_api_v1_admin_businesses__business_id__listings_accounts_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `provider` | query | ListingProvider \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ListingAccountData__ - `request_id` · string · required - `success` · true - `data` · ListingAccountData[] · required - `id` · string (uuid) · required - `business_id` · string (uuid) · required - `connection_id` · string (uuid) · required - `provider` · ListingProvider · required: Listing integration surface (``listing_accounts``, ``listings``, …). Distinct from ``OAuthProvider``. - `provider_account_id` · string · required - `provider_account_name` · string | null - `account_metadata` · object - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/listings/discovery/import Discover and import provider accounts + listings for a business Operation id: `import_listing_discovery_api_v1_admin_businesses__business_id__listings_discovery_import_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `connection_id` | query | string (uuid) | yes | OAuth connection used to call the provider API. | | `provider` | query | ListingProvider | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ListingDiscoveryImportResponseData_ - `request_id` · string · required - `success` · true - `data` · ListingDiscoveryImportResponseData · required: Summary after POST ``…/listings/discovery/import``. - `accounts_discovered` · integer · required - `locations_discovered` · integer · required - `locations_created_or_updated` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/listings/locations List provider listing locations for this business Operation id: `list_listing_locations_api_v1_admin_businesses__business_id__listings_locations_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `provider` | query | ListingProvider \| null | no | | | `is_managed` | query | boolean \| null | no | | | `internal_location_id` | query | string (uuid) \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ListingLocationData__ - `request_id` · string · required - `success` · true - `data` · ListingLocationData[] · required - `id` · string (uuid) · required - `business_id` · string (uuid) · required - `listing_account_id` · string (uuid) · required - `provider` · ListingProvider · required: Listing integration surface (``listing_accounts``, ``listings``, …). Distinct from ``OAuthProvider``. - `provider_listing_id` · string · required - `provider_resource_name` · string · required - `internal_location_id` · string (uuid) | null - `title` · string | null - `store_code` · string | null - `location_metadata` · object | null - `overview` · object | null - `synced_at` · string (date-time) | null - `is_managed` · boolean · required - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/listings/selection Replace which listings are managed for this business Operation id: `save_managed_listing_selection_route_api_v1_admin_businesses__business_id__listings_selection_patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ManagedListingSelectionBody - `managed_listing_ids` · string (uuid)[] · required - `auto_accept_keystone_invitations` · boolean: GBP: after inviting Keystone as location manager, poll and accept the pending invitation using Keystone agency OAuth (KEYSTONE_GBP_OAUTH_*). Set false to leave the invite pending for manual acceptance. **Responses** - `200` Successful Response: SuccessResponse_ManagedListingSelectionResponseData_ - `request_id` · string · required - `success` · true - `data` · ManagedListingSelectionResponseData · required: Payload for ``PATCH …/listings/selection`` — updated rows plus GBP side-effect hints. - `listings` · ListingLocationData[] · required - `accept_invite_success` · boolean · required: When ``auto_accept_keystone_invitations`` was true: false if any accept attempt failed (API error or no matching invite after retries). True when auto-accept was false or every newly managed listing accepted successfully. - `configure_notification_success` · boolean · required: False if any ``enable_notifications`` (after newly managed) or ``disable_notifications`` (after last unmanaged on an account) raised; errors are logged only. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Listing reviews 6 endpoints. HTML: https://developers.keystone.app/api/console/listing-reviews/ ### GET /api/v1/admin/businesses/{business_id}/listing-reviews List listing reviews (optionally scoped to one listing) Operation id: `list_listing_reviews_all_api_v1_admin_businesses__business_id__listing_reviews_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | query | string (uuid) \| null | no | When set, only reviews for this listing id. | | `limit` | query | integer | no | | | `cursor` | query | string \| null | no | | | `sort_by` | query | string | no | | | `rating_min` | query | integer \| null | no | | | `rating_max` | query | integer \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ListingReviewData__ - `request_id` · string · required - `success` · true - `data` · ListingReviewData[] · required - `id` · string (uuid) · required - `business_id` · string (uuid) · required - `provider` · ListingProvider · required: Listing integration surface (``listing_accounts``, ``listings``, …). Distinct from ``OAuthProvider``. - `listing_id` · string (uuid) · required - `provider_review_id` · string · required - `provider_resource_name` · string · required - `reviewer_display_name` · string | null · required - `reviewer_profile_photo_url` · string | null · required - `reviewer_is_anonymous` · boolean · required - `rating` · integer | null · required - `body` · string | null · required - `language` · string | null · required - `permalink_url` · string | null · required - `provider_created_at` · string (date-time) | null · required - `provider_updated_at` · string (date-time) | null · required - `status` · ListingReviewStatus · required: Review workflow status for ``listing_reviews``. - `review_metadata` · object | null · required - `is_featured` · boolean - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/listing-reviews/{review_id} Get one listing review Operation id: `get_listing_review_admin_api_v1_admin_businesses__business_id__listing_reviews__review_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ListingReviewData_ - `request_id` · string · required - `success` · true - `data` · ListingReviewData · required: One row from ``listing_reviews`` (provider review keyed by ``listings.id``). - `id` · string (uuid) · required - `business_id` · string (uuid) · required - `provider` · ListingProvider · required: Listing integration surface (``listing_accounts``, ``listings``, …). Distinct from ``OAuthProvider``. - `listing_id` · string (uuid) · required - `provider_review_id` · string · required - `provider_resource_name` · string · required - `reviewer_display_name` · string | null · required - `reviewer_profile_photo_url` · string | null · required - `reviewer_is_anonymous` · boolean · required - `rating` · integer | null · required - `body` · string | null · required - `language` · string | null · required - `permalink_url` · string | null · required - `provider_created_at` · string (date-time) | null · required - `provider_updated_at` · string (date-time) | null · required - `status` · ListingReviewStatus · required: Review workflow status for ``listing_reviews``. - `review_metadata` · object | null · required - `is_featured` · boolean - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/listing-reviews/{review_id} Update listing review (e.g. featured flag) Operation id: `patch_listing_review_featured_api_v1_admin_businesses__business_id__listing_reviews__review_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ListingReviewFeaturedUpdateBody - `is_featured` · boolean · required **Responses** - `200` Successful Response: SuccessResponse_ListingReviewData_ - `request_id` · string · required - `success` · true - `data` · ListingReviewData · required: One row from ``listing_reviews`` (provider review keyed by ``listings.id``). - `id` · string (uuid) · required - `business_id` · string (uuid) · required - `provider` · ListingProvider · required: Listing integration surface (``listing_accounts``, ``listings``, …). Distinct from ``OAuthProvider``. - `listing_id` · string (uuid) · required - `provider_review_id` · string · required - `provider_resource_name` · string · required - `reviewer_display_name` · string | null · required - `reviewer_profile_photo_url` · string | null · required - `reviewer_is_anonymous` · boolean · required - `rating` · integer | null · required - `body` · string | null · required - `language` · string | null · required - `permalink_url` · string | null · required - `provider_created_at` · string (date-time) | null · required - `provider_updated_at` · string (date-time) | null · required - `status` · ListingReviewStatus · required: Review workflow status for ``listing_reviews``. - `review_metadata` · object | null · required - `is_featured` · boolean - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/listing-reviews/{review_id}/replies List replies for a listing review Operation id: `list_listing_review_replies_api_v1_admin_businesses__business_id__listing_reviews__review_id__replies_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ListingReviewReplyData__ - `request_id` · string · required - `success` · true - `data` · ListingReviewReplyData[] · required - `id` · string (uuid) · required - `business_id` · string (uuid) · required - `listing_review_id` · string (uuid) · required - `provider` · ListingProvider · required: Listing integration surface (``listing_accounts``, ``listings``, …). Distinct from ``OAuthProvider``. - `provider_reply_id` · string | null - `parent_reply_id` · string (uuid) | null - `thread_root_reply_id` · string (uuid) | null - `author_type` · ListingReplyAuthorType · required: Reply author type for ``listing_review_replies``. - `provider_author_id` · string | null - `body` · string | null - `provider_created_at` · string (date-time) | null - `provider_updated_at` · string (date-time) | null - `is_deleted` · boolean · required - `reply_metadata` · object | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/listing-reviews/{review_id}/reply Create or update owner reply for a listing review Operation id: `upsert_listing_review_owner_reply_api_v1_admin_businesses__business_id__listing_reviews__review_id__reply_put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ListingReviewReplyUpsertBody - `comment` · string · required **Responses** - `200` Successful Response: SuccessResponse_ListingReviewReplyData_ - `request_id` · string · required - `success` · true - `data` · ListingReviewReplyData · required: One row from ``listing_review_replies``. - `id` · string (uuid) · required - `business_id` · string (uuid) · required - `listing_review_id` · string (uuid) · required - `provider` · ListingProvider · required: Listing integration surface (``listing_accounts``, ``listings``, …). Distinct from ``OAuthProvider``. - `provider_reply_id` · string | null - `parent_reply_id` · string (uuid) | null - `thread_root_reply_id` · string (uuid) | null - `author_type` · ListingReplyAuthorType · required: Reply author type for ``listing_review_replies``. - `provider_author_id` · string | null - `body` · string | null - `provider_created_at` · string (date-time) | null - `provider_updated_at` · string (date-time) | null - `is_deleted` · boolean · required - `reply_metadata` · object | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/listing-reviews/{review_id}/reply Delete owner reply for a listing review Operation id: `delete_listing_review_owner_reply_api_v1_admin_businesses__business_id__listing_reviews__review_id__reply_delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Listings 11 endpoints. HTML: https://developers.keystone.app/api/console/listings/ ### GET /api/v1/admin/businesses/{business_id}/listings List managed GBP listings grouped by internal location Operation id: `list_managed_listings_api_v1_admin_businesses__business_id__listings_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ManagedListingsData_ - `request_id` · string · required - `success` · true - `data` · ManagedListingsData · required - `groups` · ListingLocationGroup[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/listings/{listing_id} Get managed listing detail (overview from GBP Business Information) Operation id: `get_listing_detail_api_v1_admin_businesses__business_id__listings__listing_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ListingDetailData_ - `request_id` · string · required - `success` · true - `data` · ListingDetailData · required: Single managed listing for the Overview tab (``listing_id`` = ``gbp_locations.id``). - `listing_id` · string (uuid) · required - `business_id` · string (uuid) · required - `gbp_location_id` · string · required - `full_resource_name` · string · required - `internal_location_id` · string (uuid) | null · required - `is_managed` · boolean · required - `title` · string | null · required - `store_code` · string | null · required - `overview` · ListingOverviewData | null · required - `platform_info` · ListingPlatformInfo | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/listings/{listing_id}/photos List photos linked to a managed GBP listing Operation id: `get_listing_photos_api_v1_admin_businesses__business_id__listings__listing_id__photos_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ListingPhotoItem__ - `request_id` · string · required - `success` · true - `data` · ListingPhotoItem[] · required - `association_id` · string (uuid) · required - `photo_id` · string (uuid) · required - `photo_url` · string | null - `is_deleted` · boolean · required: Library/photo row soft-delete; association is still active until removed. - `photo_source` · string · required - `format` · string | null - `width` · integer | null - `height` · integer | null - `duration` · number | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/listings/{listing_id}/photos Add a library photo to a GBP listing (uploads to GBP then links) Operation id: `add_listing_photo_api_v1_admin_businesses__business_id__listings__listing_id__photos_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AddListingPhotoBody - `photo_id` · string (uuid) · required **Responses** - `200` Successful Response: SuccessResponse_ListingPhotoItem_ - `request_id` · string · required - `success` · true - `data` · ListingPhotoItem · required: One photo on a managed listing (active ``listing_media`` + ``photos`` row). - `association_id` · string (uuid) · required - `photo_id` · string (uuid) · required - `photo_url` · string | null - `is_deleted` · boolean · required: Library/photo row soft-delete; association is still active until removed. - `photo_source` · string · required - `format` · string | null - `width` · integer | null - `height` · integer | null - `duration` · number | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/listings/{listing_id}/photos/{photo_id} Remove a photo from the listing (association soft-delete only) Operation id: `remove_listing_photo_api_v1_admin_businesses__business_id__listings__listing_id__photos__photo_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `photo_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/listings/{listing_id}/photos/sync Enqueue GBP listing media import for one managed listing Operation id: `sync_listing_photos_from_gbp_api_v1_admin_businesses__business_id__listings__listing_id__photos_sync_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ListingPhotosSyncBody - `sync_type` · ListingSyncType: Sync run type for listing sync logs. - `max_pages_per_channel` · integer | null: Max list API pages per channel (owner ``media.list`` and customer ``media.customers.list``); omit for all pages until exhausted. - `page_size` · integer: Items per GBP list request (capped per provider). **Responses** - `200` Successful Response: SuccessResponse_ListingPhotosSyncEnqueuedData_ - `request_id` · string · required - `success` · true - `data` · ListingPhotosSyncEnqueuedData · required: Admin API response after enqueueing GBP listing media import (same pattern as review sync enqueue). - `gbp_location_row_id` · string (uuid) · required - `sync_log_id` · string (uuid) · required - `sync_type` · ListingSyncType · required: Sync run type for listing sync logs. - `title` · string | null - `enqueued` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/listings/{listing_id}/photos/sync-logs/{sync_log_id}/resume Resume or retry a PARTIAL/FAILED GBP listing media sync log (background) Operation id: `resume_listing_photos_sync_from_gbp_api_v1_admin_businesses__business_id__listings__listing_id__photos_sync_logs__sync_log_id__resume_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `sync_log_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ListingPhotosSyncResumeBody - `max_pages_per_channel` · integer | null: Cap GBP list pages **per channel** for this resume segment. - `page_size` · integer: Items per GBP list request. **Responses** - `200` Successful Response: SuccessResponse_ListingPhotosSyncResumeEnqueuedData_ - `request_id` · string · required - `success` · true - `data` · ListingPhotosSyncResumeEnqueuedData · required: Admin API response after accepting resume/retry for a listing media sync log. - `gbp_location_row_id` · string (uuid) · required - `sync_log_id` · string (uuid) · required - `title` · string | null - `enqueued` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/listings/{listing_id}/reviews List provider reviews stored for a managed listing Operation id: `list_listing_reviews_api_v1_admin_businesses__business_id__listings__listing_id__reviews_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `cursor` | query | string \| null | no | | | `sort_by` | query | string | no | | | `rating_min` | query | integer \| null | no | | | `rating_max` | query | integer \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ListingReviewData__ - `request_id` · string · required - `success` · true - `data` · ListingReviewData[] · required - `id` · string (uuid) · required - `business_id` · string (uuid) · required - `provider` · ListingProvider · required: Listing integration surface (``listing_accounts``, ``listings``, …). Distinct from ``OAuthProvider``. - `listing_id` · string (uuid) · required - `provider_review_id` · string · required - `provider_resource_name` · string · required - `reviewer_display_name` · string | null · required - `reviewer_profile_photo_url` · string | null · required - `reviewer_is_anonymous` · boolean · required - `rating` · integer | null · required - `body` · string | null · required - `language` · string | null · required - `permalink_url` · string | null · required - `provider_created_at` · string (date-time) | null · required - `provider_updated_at` · string (date-time) | null · required - `status` · ListingReviewStatus · required: Review workflow status for ``listing_reviews``. - `review_metadata` · object | null · required - `is_featured` · boolean - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/listings/{listing_id}/reviews/sync Enqueue provider review sync for one managed listing Operation id: `sync_listing_reviews_api_v1_admin_businesses__business_id__listings__listing_id__reviews_sync_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ListingReviewsSyncBody - `sync_type` · ListingSyncType: Sync run type for listing sync logs. - `max_pages` · integer | null: Cap provider list pages for this run (omit for natural stop). - `page_size` · integer: Reviews per list page. **Responses** - `200` Successful Response: SuccessResponse_ListingReviewsSyncEnqueuedData_ - `request_id` · string · required - `success` · true - `data` · ListingReviewsSyncEnqueuedData · required - `listing_id` · string (uuid) · required - `sync_log_id` · string (uuid) · required - `sync_type` · ListingSyncType · required: Sync run type for listing sync logs. - `title` · string | null - `enqueued` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/listings/{listing_id}/reviews/sync-logs/{sync_log_id}/resume Resume or retry a PARTIAL/FAILED listing review sync log Operation id: `resume_listing_reviews_sync_api_v1_admin_businesses__business_id__listings__listing_id__reviews_sync_logs__sync_log_id__resume_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `sync_log_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ListingReviewsSyncResumeBody - `max_pages` · integer | null - `page_size` · integer **Responses** - `200` Successful Response: SuccessResponse_ListingReviewsSyncResumeEnqueuedData_ - `request_id` · string · required - `success` · true - `data` · ListingReviewsSyncResumeEnqueuedData · required - `listing_id` · string (uuid) · required - `sync_log_id` · string (uuid) · required - `title` · string | null - `enqueued` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/listings/gbp/refresh-all Re-fetch overview + platform metadata for EVERY GBP listing (all businesses) Operation id: `refresh_all_gbp_listings_api_v1_admin_listings_gbp_refresh_all_post` Fleet-wide backfill: pull each GBP listing's live overview + ``metadata`` block (``placeId`` / ``mapsUri`` / ``newReviewUri``) and persist it. One call fixes every business whose ``location_metadata`` was frozen stale at discovery time, so the conversational agent's Google-review link resolves per business (KS-866). Returns ``{total, refreshed, failed}``; per-listing failures are isolated (savepoint) and do not abort the run. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Listings agent 2 endpoints. HTML: https://developers.keystone.app/api/console/listings-agent/ ### GET /api/v1/businesses/{business_id}/listings-agent/config Get effective listings-agent settings for a business Operation id: `get_config_api_v1_businesses__business_id__listings_agent_config_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SimpleAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · SimpleAgentConfigData · required: Shared shape for contacts / ads / listings agents. - `business_id` · string · required - `enabled` · boolean · required - `instructions` · string - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/businesses/{business_id}/listings-agent/config Update listings-agent settings for a business Operation id: `patch_config_api_v1_businesses__business_id__listings_agent_config_patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): SimpleAgentConfigPatchBody - `enabled` · boolean | null - `instructions` · string | null **Responses** - `200` Successful Response: SuccessResponse_SimpleAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · SimpleAgentConfigData · required: Shared shape for contacts / ads / listings agents. - `business_id` · string · required - `enabled` · boolean · required - `instructions` · string - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Locations 6 endpoints. HTML: https://developers.keystone.app/api/console/locations/ ### GET /api/v1/admin/businesses/{business_id}/locations Get all locations Operation id: `list_locations_api_v1_admin_businesses__business_id__locations_get` Get all locations for a business. By default only active; set listInactive=true to include inactive. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `list_inactive` | query | boolean | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_LocationData__ - `request_id` · string · required - `success` · true - `data` · LocationData[] · required - `id` · string · required - `business_id` · string · required - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `slug` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/locations Create location Operation id: `create_location_api_v1_admin_businesses__business_id__locations_post` Create a new location. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): LocationCreateBody - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `line1` · string | null - `line2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `country` · string | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `monday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `tuesday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `wednesday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `thursday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `friday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `saturday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `sunday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `slug` · string | null - `photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_LocationData_ - `request_id` · string · required - `success` · true - `data` · LocationData · required: Location for list/GET response. - `id` · string · required - `business_id` · string · required - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `slug` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/locations/{location_id} Get location details Operation id: `get_location_api_v1_admin_businesses__business_id__locations__location_id__get` Get location by id. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `location_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_LocationData_ - `request_id` · string · required - `success` · true - `data` · LocationData · required: Location for list/GET response. - `id` · string · required - `business_id` · string · required - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `slug` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/locations/{location_id} Update location Operation id: `update_location_api_v1_admin_businesses__business_id__locations__location_id__put` Update location details. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `location_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): LocationUpdateBody - `location_name` · string | null - `is_primary_location` · boolean | null - `is_active_location` · boolean | null - `address` · Address | null - `line1` · string | null - `line2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `country` · string | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `monday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `tuesday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `wednesday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `thursday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `friday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `saturday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `sunday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `slug` · string | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_LocationData_ - `request_id` · string · required - `success` · true - `data` · LocationData · required: Location for list/GET response. - `id` · string · required - `business_id` · string · required - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `slug` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/locations/{location_id} Delete location Operation id: `delete_location_api_v1_admin_businesses__business_id__locations__location_id__delete` Delete a location. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `location_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/locations/reorder Reorder locations Operation id: `reorder_locations_api_v1_admin_businesses__business_id__locations_reorder_put` Reorder locations by ordered list of location ids. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): LocationReorderBody - `ordered_location_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_list_LocationData__ - `request_id` · string · required - `success` · true - `data` · LocationData[] · required - `id` · string · required - `business_id` · string · required - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `slug` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Maps 5 endpoints. HTML: https://developers.keystone.app/api/console/maps/ ### GET /api/v1/admin/businesses/{business_id}/listings/{listing_id}/maps/performance Per-listing Performance tab: 30-day metrics, keywords, tracked queries Operation id: `get_performance_api_v1_admin_businesses__business_id__listings__listing_id__maps_performance_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PerformanceData_ - `request_id` · string · required - `success` · true - `data` · PerformanceData · required - `businessId` · string (uuid) · required - `listingId` · string (uuid) · required - `generatedAt` · string (date-time) · required - `windowDays` · integer · required - `state` · "ok" | "syncing" | "forbidden" | "rankings_disabled" | "unavailable" · required - `groups` · MetricGroup[] · required - `keywordsMonth` · string (date) | null - `keywords` · KeywordRow[] · required - `queriesState` · "ok" | "syncing" | "forbidden" | "rankings_disabled" | "unavailable" · required - `queries` · TrackedQuery[] · required - `volumeArea` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/listings/{listing_id}/maps/profile-data Per-listing Profile data tab: the audit inventory with rule hints Operation id: `get_profile_data_api_v1_admin_businesses__business_id__listings__listing_id__maps_profile_data_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ProfileData_ - `request_id` · string · required - `success` · true - `data` · ProfileData · required - `businessId` · string (uuid) · required - `listingId` · string (uuid) · required - `generatedAt` · string (date-time) · required - `state` · "ok" | "syncing" | "forbidden" | "rankings_disabled" | "unavailable" · required - `auditedOn` · string (date) | null - `groups` · ProfileGroup[] · required - `rawAvailable` · boolean - `extra` · object | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/listings/{listing_id}/maps/readiness Per-listing Readiness tab: score, categories, recommendations Operation id: `get_readiness_api_v1_admin_businesses__business_id__listings__listing_id__maps_readiness_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ReadinessData_ - `request_id` · string · required - `success` · true - `data` · ReadinessData · required - `businessId` · string (uuid) · required - `listingId` · string (uuid) · required - `generatedAt` · string (date-time) · required - `state` · "ok" | "syncing" | "forbidden" | "rankings_disabled" | "unavailable" · required - `scoredOn` · string (date) | null - `overall` · MapsCategoryScore | null - `categories` · MapsCategoryScore[] · required - `recommendations` · Recommendation[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/listings/{listing_id}/maps/refresh Populate Maps analytics for one listing now (audit → performance → quality → score) Operation id: `refresh_listing_api_v1_admin_businesses__business_id__listings__listing_id__maps_refresh_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `listing_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `202` Successful Response: SuccessResponse_RefreshData_ - `request_id` · string · required - `success` · true - `data` · RefreshData · required - `scheduled` · boolean · required - `reason` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/maps/summary Maps performance across all managed listings of a business Operation id: `get_summary_api_v1_admin_businesses__business_id__maps_summary_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SummaryData_ - `request_id` · string · required - `success` · true - `data` · SummaryData · required - `businessId` · string (uuid) · required - `generatedAt` · string (date-time) · required - `windowDays` · integer · required - `trends` · Trend[] · required - `rows` · MetricRow[] · required - `groups` · MetricGroup[] · required - `listings` · ListingSummary[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Meta Conversions API 4 endpoints. HTML: https://developers.keystone.app/api/console/meta-capi/ ### GET /api/v1/admin/businesses/{business_id}/integrations/meta/capi Get Meta CAPI config for a business Operation id: `get_meta_capi_config_api_v1_admin_businesses__business_id__integrations_meta_capi_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_MetaCapiConfigData_ - `request_id` · string · required - `success` · true - `data` · MetaCapiConfigData · required: CAPI config returned to admins. No token field — CAPI reuses the business' existing Meta OAuth token. - `pixel_id` · string | null - `test_event_code` · string | null - `enabled` · boolean - `connected` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/integrations/meta/capi Set Meta CAPI config (pixel id, enable, test event code) Operation id: `put_meta_capi_config_api_v1_admin_businesses__business_id__integrations_meta_capi_put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): MetaCapiConfigBody - `pixel_id` · string · required - `test_event_code` · string | null - `enabled` · boolean **Responses** - `200` Successful Response: SuccessResponse_MetaCapiConfigData_ - `request_id` · string · required - `success` · true - `data` · MetaCapiConfigData · required: CAPI config returned to admins. No token field — CAPI reuses the business' existing Meta OAuth token. - `pixel_id` · string | null - `test_event_code` · string | null - `enabled` · boolean - `connected` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/integrations/meta/capi/pixels Discover Meta pixels on a business' connected ad account Operation id: `list_meta_capi_pixels_api_v1_admin_businesses__business_id__integrations_meta_capi_pixels_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_MetaCapiPixelsData_ - `request_id` · string · required - `success` · true - `data` · MetaCapiPixelsData · required: Pixels available on the business' connected Meta ad account. - `pixels` · MetaPixelData[] · required - `connected` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/integrations/meta/capi/test Fire a test CAPI event (validate sender via Events Manager Test Events) Operation id: `fire_meta_capi_test_api_v1_admin_businesses__business_id__integrations_meta_capi_test_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): MetaCapiTestBody - `event_name` · string - `email` · string | null **Responses** - `200` Successful Response: SuccessResponse_MetaCapiTestResult_ - `request_id` · string · required - `success` · true - `data` · MetaCapiTestResult · required: Echo of Meta's /events response for a test fire. - `events_received` · integer - `fbtrace_id` · string | null - `messages` · string[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: OAuth connections 4 endpoints. HTML: https://developers.keystone.app/api/console/oauth-connections/ ### GET /api/v1/admin/businesses/{business_id}/connections List active OAuth connections for a business Operation id: `list_connections_api_v1_admin_businesses__business_id__connections_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `provider` | query | OAuthProvider \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_OAuthConnectionsListData_ - `request_id` · string · required - `success` · true - `data` · OAuthConnectionsListData · required - `connections` · OAuthConnectionData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/connections/{connection_id} Disconnect an OAuth connection Operation id: `disconnect_connection_api_v1_admin_businesses__business_id__connections__connection_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `connection_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/connections/oauth/{provider}/start Start OAuth for an authority (e.g. Google) Operation id: `oauth_start_api_v1_admin_businesses__business_id__connections_oauth__provider__start_post` Return an authorization URL for the given OAuth authority (admin UI redirect). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `provider` | path | OAuthProvider | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): OAuthStartBody - `redirect_uri` · string | null: Optional redirect URI; must match a Google-authorized callback when using Google. **Responses** - `200` Successful Response: SuccessResponse_OAuthStartResponseData_ - `request_id` · string · required - `success` · true - `data` · OAuthStartResponseData · required - `authorization_url` · string · required - `state` · string · required - `connection_id` · string (uuid) | null: Present when exactly one active connection exists for this business and provider. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/connections/oauth/{provider}/callback OAuth callback (browser redirect target) Operation id: `oauth_callback_api_v1_admin_connections_oauth__provider__callback_get` Exchange authorization code for tokens and upsert ``oauth_connections`` (no JWT: IDP redirect). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `provider` | path | OAuthProvider | yes | | | `code` | query | string \| null | no | | | `state` | query | string \| null | no | | | `error` | query | string \| null | no | | | `error_description` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Offers 5 endpoints. HTML: https://developers.keystone.app/api/console/offers/ ### PUT /api/v1/admin/businesses/{business_id}/offers/{offer_id} Update an offer Operation id: `update_offer_api_v1_admin_businesses__business_id__offers__offer_id__put` Update an offer's fields. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `offer_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): OfferUpdateBody - `offer_name` · string | null - `offer_description` · string | null - `value_terms` · string | null - `expires_at` · integer | null - `sort_order` · integer | null - `active` · boolean | null **Responses** - `200` Successful Response: SuccessResponse_OfferData_ - `request_id` · string · required - `success` · true - `data` · OfferData · required: Offer for response. Nested under PackageData.offers and ServiceItemData.offers. - `id` · string · required - `business_id` · string · required - `offer_name` · string · required - `offer_description` · string | null - `value_terms` · string | null - `expires_at` · integer | null - `sort_order` · integer - `active` · boolean - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/packages/{package_id}/offers Attach a new offer to a package Operation id: `add_offer_to_package_api_v1_admin_businesses__business_id__packages__package_id__offers_post` Create an offer and link it to the package. 404 if package not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): OfferCreateBody - `offer_name` · string · required - `offer_description` · string | null - `value_terms` · string | null - `expires_at` · integer | null - `sort_order` · integer - `active` · boolean **Responses** - `201` Successful Response: SuccessResponse_OfferData_ - `request_id` · string · required - `success` · true - `data` · OfferData · required: Offer for response. Nested under PackageData.offers and ServiceItemData.offers. - `id` · string · required - `business_id` · string · required - `offer_name` · string · required - `offer_description` · string | null - `value_terms` · string | null - `expires_at` · integer | null - `sort_order` · integer - `active` · boolean - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/packages/{package_id}/offers/{offer_id} Detach an offer from a package Operation id: `remove_offer_from_package_api_v1_admin_businesses__business_id__packages__package_id__offers__offer_id__delete` Unlink an offer from a package (and reap it if now orphaned). 404 if not linked. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `offer_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/service-items/{service_item_id}/offers Attach a new offer to a service item Operation id: `add_offer_to_service_item_api_v1_admin_businesses__business_id__service_items__service_item_id__offers_post` Create an offer and link it to the service item. 404 if service item not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `service_item_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): OfferCreateBody - `offer_name` · string · required - `offer_description` · string | null - `value_terms` · string | null - `expires_at` · integer | null - `sort_order` · integer - `active` · boolean **Responses** - `201` Successful Response: SuccessResponse_OfferData_ - `request_id` · string · required - `success` · true - `data` · OfferData · required: Offer for response. Nested under PackageData.offers and ServiceItemData.offers. - `id` · string · required - `business_id` · string · required - `offer_name` · string · required - `offer_description` · string | null - `value_terms` · string | null - `expires_at` · integer | null - `sort_order` · integer - `active` · boolean - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/service-items/{service_item_id}/offers/{offer_id} Detach an offer from a service item Operation id: `remove_offer_from_service_item_api_v1_admin_businesses__business_id__service_items__service_item_id__offers__offer_id__delete` Unlink an offer from a service item (and reap it if now orphaned). 404 if not linked. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `service_item_id` | path | string (uuid) | yes | | | `offer_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Packages 9 endpoints. HTML: https://developers.keystone.app/api/console/packages/ ### GET /api/v1/admin/businesses/{business_id}/packages Get all packages Operation id: `list_packages_api_v1_admin_businesses__business_id__packages_get` Get all packages for a business (ordered by display_order). Member service items not included. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PackageData__ - `request_id` · string · required - `success` · true - `data` · PackageData[] · required - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/packages Create package Operation id: `create_package_api_v1_admin_businesses__business_id__packages_post` Create a new package. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): PackageCreateBody - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · number | string | null - `slug` · string | null - `photo_ids` · string[] - `is_featured` · boolean | null **Responses** - `201` Successful Response: SuccessResponse_PackageData_ - `request_id` · string · required - `success` · true - `data` · PackageData · required: Package for list/GET response. `service_items` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-package member queries. - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/packages/{package_id} Get package details Operation id: `get_package_api_v1_admin_businesses__business_id__packages__package_id__get` Get package by id with member service items. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PackageData_ - `request_id` · string · required - `success` · true - `data` · PackageData · required: Package for list/GET response. `service_items` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-package member queries. - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/packages/{package_id} Update package Operation id: `update_package_api_v1_admin_businesses__business_id__packages__package_id__put` Update package details. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): PackageUpdateBody - `package_name` · string | null - `package_description` · string | null - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · number | string | null - `slug` · string | null - `photo_ids` · string[] | null - `is_featured` · boolean | null **Responses** - `200` Successful Response: SuccessResponse_PackageData_ - `request_id` · string · required - `success` · true - `data` · PackageData · required: Package for list/GET response. `service_items` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-package member queries. - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/packages/{package_id} Delete package Operation id: `delete_package_api_v1_admin_businesses__business_id__packages__package_id__delete` Delete a package. Member service items survive (join rows cascade). 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/packages/{package_id}/service-items Set package member service items Operation id: `set_package_service_items_api_v1_admin_businesses__business_id__packages__package_id__service_items_put` Replace the package's member service items (and their in-package order). 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): PackageServiceItemsBody - `ordered_service_item_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_PackageData_ - `request_id` · string · required - `success` · true - `data` · PackageData · required: Package for list/GET response. `service_items` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-package member queries. - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/packages/{package_id}/service-items/{service_item_id} Add service item to package Operation id: `add_service_item_to_package_api_v1_admin_businesses__business_id__packages__package_id__service_items__service_item_id__post` Add a service item to a package (appended last). 404 if package not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `service_item_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/packages/{package_id}/service-items/{service_item_id} Remove service item from package Operation id: `remove_service_item_from_package_api_v1_admin_businesses__business_id__packages__package_id__service_items__service_item_id__delete` Remove a service item from a package. 404 if package or membership not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `service_item_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/packages/reorder Reorder packages Operation id: `reorder_packages_api_v1_admin_businesses__business_id__packages_reorder_put` Reorder packages by ordered list of package ids. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): PackageReorderBody - `ordered_package_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_list_PackageData__ - `request_id` · string · required - `success` · true - `data` · PackageData[] · required - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Photos 28 endpoints. HTML: https://developers.keystone.app/api/console/photos/ ### GET /api/v1/admin/businesses/{business_id}/generated List generated photos for a business Operation id: `list_generated_photos_api_v1_admin_businesses__business_id__generated_get` List all generated photos across generation jobs. Pagination via cursor and limit. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_GeneratedPhotoData__ - `request_id` · string · required - `success` · true - `data` · GeneratedPhotoData[] · required - `id` · string · required: generated_photo_id (job_id:index) - `business_id` · string · required - `photo_source` · string - `public_url` · string | null: Public CDN URL of the original (no auth); null when not CDN-addressable. - `photo_metadata` · PhotoMetadata | null - `generation_status` · string · required: Job status: queued \| processing \| completed \| failed \| stopped - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/generated/{generated_photo_id} Serve generated photo file from GCS Operation id: `get_generated_photo_file_api_v1_admin_businesses__business_id__generated__generated_photo_id__get` Fetch generated photo bytes from GCS. generated_photo_id is 'job_id:index'. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `generated_photo_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: string (binary) - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/photos Get business photo library Operation id: `list_photos_api_v1_admin_businesses__business_id__photos_get` Get business library photos. Pagination via cursor (string) and limit. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PhotoData__ - `request_id` · string · required - `success` · true - `data` · PhotoData[] · required - `id` · string · required - `business_id` · string · required - `photo_url` · string | null - `public_url` · string | null: Public CDN URL of the original (no auth); null when the photo isn't CDN-addressable. - `public_url_downloadable` · boolean | null: Whether browser fetch of public_url works (CORS). False for legacy R2 hosts (copy-only); null when public_url is null. - `photo_source` · string · required - `original_filename` · string | null - `photo_metadata` · PhotoMetadata | null - `is_deleted` · boolean - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/photos/{photo_id} Get photo metadata Operation id: `get_photo_metadata_api_v1_admin_businesses__business_id__photos__photo_id__get` Metadata (incl. ``public_url``) for one photo — the JSON counterpart of the byte-serving ``…/photos/get/{photo_id}``. 404 for missing, deleted, or cross-tenant photos. Declared after the literal ``/photos/*`` routes so it never shadows them. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `photo_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PhotoData_ - `request_id` · string · required - `success` · true - `data` · PhotoData · required: Photo for list/GET response. - `id` · string · required - `business_id` · string · required - `photo_url` · string | null - `public_url` · string | null: Public CDN URL of the original (no auth); null when the photo isn't CDN-addressable. - `public_url_downloadable` · boolean | null: Whether browser fetch of public_url works (CORS). False for legacy R2 hosts (copy-only); null when public_url is null. - `photo_source` · string · required - `original_filename` · string | null - `photo_metadata` · PhotoMetadata | null - `is_deleted` · boolean - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/photos/{photo_id} Delete photo from library Operation id: `delete_photo_api_v1_admin_businesses__business_id__photos__photo_id__delete` Soft-delete photo from business library. 404 if not found; 409 PHOTO_IN_USE (with per-module usage breakdown in ``details``) if still referenced by a module. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `photo_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/photos/generate Generate photos Operation id: `generate_photos_api_v1_admin_businesses__business_id__photos_generate_post` Generate photos from description/style. Returns job ID to poll for status. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): GeneratePhotosBody - `description` · string · required - `style` · string | null - `aspect_ratio` · string | null **Responses** - `202` Successful Response: SuccessResponse_GenerateJobResponse_ - `request_id` · string · required - `success` · true - `data` · GenerateJobResponse · required: Response after submitting a photo generation job. - `job_id` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/photos/generate/{job_id} Get photo generation job status Operation id: `get_generation_status_api_v1_admin_businesses__business_id__photos_generate__job_id__get` Get generation job status. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `job_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_GenerateJobStatusData_ - `request_id` · string · required - `success` · true - `data` · GenerateJobStatusData · required: Generation job status. - `job_id` · string · required - `status` · "queued" | "processing" | "completed" | "failed" | "stopped" · required: queued \| processing \| completed \| failed \| stopped - `created_at` · integer | null - `generated_photo_ids` · string[] | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/photos/get/{photo_id} Serve photo file from GCS Operation id: `get_photo_file_api_v1_admin_businesses__business_id__photos_get__photo_id__get` Fetch photo bytes from GCS and return as binary response. Requires viewer role. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `photo_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: string (binary) - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/photos/import-from-gallery Import photos from industry gallery Operation id: `import_from_gallery_api_v1_admin_businesses__business_id__photos_import_from_gallery_post` Import selected industry gallery photos into business library. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ImportFromGalleryBody - `gallery_photo_ids` · string[] · required **Responses** - `201` Successful Response: SuccessResponse_list_PhotoData__ - `request_id` · string · required - `success` · true - `data` · PhotoData[] · required - `id` · string · required - `business_id` · string · required - `photo_url` · string | null - `public_url` · string | null: Public CDN URL of the original (no auth); null when the photo isn't CDN-addressable. - `public_url_downloadable` · boolean | null: Whether browser fetch of public_url works (CORS). False for legacy R2 hosts (copy-only); null when public_url is null. - `photo_source` · string · required - `original_filename` · string | null - `photo_metadata` · PhotoMetadata | null - `is_deleted` · boolean - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/photos/import-from-unsplash Import photos from Unsplash Operation id: `import_from_unsplash_api_v1_admin_businesses__business_id__photos_import_from_unsplash_post` Import photos from Unsplash by IDs or URLs (hotlinked; no upload). IDs require UNSPLASH_ACCESS_KEY. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ImportFromUnsplashBody - `unsplash_photo_ids` · string[] | null - `unsplash_photo_urls` · string[] | null **Responses** - `201` Successful Response: SuccessResponse_list_PhotoData__ - `request_id` · string · required - `success` · true - `data` · PhotoData[] · required - `id` · string · required - `business_id` · string · required - `photo_url` · string | null - `public_url` · string | null: Public CDN URL of the original (no auth); null when the photo isn't CDN-addressable. - `public_url_downloadable` · boolean | null: Whether browser fetch of public_url works (CORS). False for legacy R2 hosts (copy-only); null when public_url is null. - `photo_source` · string · required - `original_filename` · string | null - `photo_metadata` · PhotoMetadata | null - `is_deleted` · boolean - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/photos/import-generated Import from generated photos Operation id: `import_generated_api_v1_admin_businesses__business_id__photos_import_generated_post` Import selected generated photos (by job_id:index) into business library. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ImportGeneratedBody - `generated_photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_list_PhotoData__ - `request_id` · string · required - `success` · true - `data` · PhotoData[] · required - `id` · string · required - `business_id` · string · required - `photo_url` · string | null - `public_url` · string | null: Public CDN URL of the original (no auth); null when the photo isn't CDN-addressable. - `public_url_downloadable` · boolean | null: Whether browser fetch of public_url works (CORS). False for legacy R2 hosts (copy-only); null when public_url is null. - `photo_source` · string · required - `original_filename` · string | null - `photo_metadata` · PhotoMetadata | null - `is_deleted` · boolean - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/photos/industry-gallery Get industry gallery photos Operation id: `list_industry_gallery_api_v1_admin_businesses__business_id__photos_industry_gallery_get` Get photos from industry gallery. Optional industryId filter. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `industry_id` | query | string \| null | no | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_IndustryGalleryPhotoData__ - `request_id` · string · required - `success` · true - `data` · IndustryGalleryPhotoData[] · required - `id` · string · required - `industry_id` · string | null - `photo_url` · string · required - `public_url` · string | null: Public CDN URL of the original (no auth); null when not CDN-addressable. - `public_url_downloadable` · boolean | null: Whether browser fetch of public_url works (CORS). False for legacy R2 hosts (copy-only); null when public_url is null. - `photo_metadata` · PhotoMetadata | null - `created_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/photos/industry-gallery/get/{photo_id} Serve industry gallery photo file from GCS Operation id: `get_industry_gallery_photo_file_api_v1_admin_businesses__business_id__photos_industry_gallery_get__photo_id__get` Fetch industry gallery photo bytes from GCS and return as binary response. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `photo_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: string (binary) - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/photos/search-unsplash Search Unsplash Operation id: `search_unsplash_api_v1_admin_businesses__business_id__photos_search_unsplash_get` Search Unsplash. Requires UNSPLASH_ACCESS_KEY. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `query` | query | string | yes | Search term | | `page` | query | integer | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_UnsplashSearchData_ - `request_id` · string · required - `success` · true - `data` · UnsplashSearchData · required: Unsplash search response (stub). - `items` · UnsplashSearchResultItem[] · required - `page` · integer - `total_pages` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/photos/upload Upload photo to business library Operation id: `upload_photo_api_v1_admin_businesses__business_id__photos_upload_post` Upload a photo (multipart/form-data). Requires GCS bucket configured. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (multipart/form-data, required): Body_upload_photo_api_v1_admin_businesses__business_id__photos_upload_post - `file` · string · required: Image file **Responses** - `201` Successful Response: SuccessResponse_PhotoData_ - `request_id` · string · required - `success` · true - `data` · PhotoData · required: Photo for list/GET response. - `id` · string · required - `business_id` · string · required - `photo_url` · string | null - `public_url` · string | null: Public CDN URL of the original (no auth); null when the photo isn't CDN-addressable. - `public_url_downloadable` · boolean | null: Whether browser fetch of public_url works (CORS). False for legacy R2 hosts (copy-only); null when public_url is null. - `photo_source` · string · required - `original_filename` · string | null - `photo_metadata` · PhotoMetadata | null - `is_deleted` · boolean - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/industries/{industry_id}/generated List generated photos for an industry Operation id: `list_industry_generated_photos_api_v1_admin_industries__industry_id__generated_get` List all generated photos across generation jobs for an industry. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_IndustryGeneratedPhotoData__ - `request_id` · string · required - `success` · true - `data` · IndustryGeneratedPhotoData[] · required - `id` · string · required: generated_photo_id (job_id:index) - `industry_id` · string · required - `photo_source` · string - `public_url` · string | null: Public CDN URL of the original (no auth); null when not CDN-addressable. - `photo_metadata` · PhotoMetadata | null - `generation_status` · string · required: Job status: queued \| processing \| completed \| failed \| stopped - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/industries/{industry_id}/generated/{generated_photo_id} Serve generated photo file from GCS Operation id: `get_industry_generated_photo_file_api_v1_admin_industries__industry_id__generated__generated_photo_id__get` Fetch generated photo bytes from GCS. generated_photo_id is 'job_id:index'. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `generated_photo_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/industries/{industry_id}/photos List photos for an industry Operation id: `list_industry_photos_api_v1_admin_industries__industry_id__photos_get` List photos scoped to a single industry with cursor pagination. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_IndustryGalleryPhotoData__ - `request_id` · string · required - `success` · true - `data` · IndustryGalleryPhotoData[] · required - `id` · string · required - `industry_id` · string | null - `photo_url` · string · required - `public_url` · string | null: Public CDN URL of the original (no auth); null when not CDN-addressable. - `public_url_downloadable` · boolean | null: Whether browser fetch of public_url works (CORS). False for legacy R2 hosts (copy-only); null when public_url is null. - `photo_metadata` · PhotoMetadata | null - `created_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/industries/{industry_id}/photos Delete industry gallery photos by id Operation id: `delete_industry_gallery_photos_api_v1_admin_industries__industry_id__photos_delete` Remove gallery rows whose ids are listed and belong to this industry (JSON body). Body may be a JSON array of id strings or ``{"ids": [...]}`` (legacy ``gallery_photo_ids`` also accepted). If you get 422 with an empty/missing body, some clients or proxies drop bodies on **DELETE**; use ``POST .../photos/delete`` with the same JSON instead. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/industries/{industry_id}/photos/delete Delete industry gallery photos (POST; use if DELETE body is stripped) Operation id: `delete_industry_gallery_photos_via_post_api_v1_admin_industries__industry_id__photos_delete_post` Same behavior as ``DELETE /industries/{industry_id}/photos``; POST keeps the JSON body on strict proxies. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/industries/{industry_id}/photos/generate Generate photos Operation id: `generate_industry_photos_api_v1_admin_industries__industry_id__photos_generate_post` Generate photos from description/style for an industry. Returns job ID to poll for status. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): GeneratePhotosBody - `description` · string · required - `style` · string | null - `aspect_ratio` · string | null **Responses** - `202` Successful Response: SuccessResponse_GenerateJobResponse_ - `request_id` · string · required - `success` · true - `data` · GenerateJobResponse · required: Response after submitting a photo generation job. - `job_id` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/industries/{industry_id}/photos/generate/{job_id} Get photo generation job status Operation id: `get_industry_generation_status_api_v1_admin_industries__industry_id__photos_generate__job_id__get` Get generation job status for an industry. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `job_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_GenerateJobStatusData_ - `request_id` · string · required - `success` · true - `data` · GenerateJobStatusData · required: Generation job status. - `job_id` · string · required - `status` · "queued" | "processing" | "completed" | "failed" | "stopped" · required: queued \| processing \| completed \| failed \| stopped - `created_at` · integer | null - `generated_photo_ids` · string[] | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/industries/{industry_id}/photos/import-from-unsplash Import photos from Unsplash into industry gallery Operation id: `import_industry_from_unsplash_api_v1_admin_industries__industry_id__photos_import_from_unsplash_post` Import photos from Unsplash by IDs or URLs (hotlinked; no upload). IDs require UNSPLASH_ACCESS_KEY. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ImportFromUnsplashBody - `unsplash_photo_ids` · string[] | null - `unsplash_photo_urls` · string[] | null **Responses** - `201` Successful Response: SuccessResponse_list_IndustryGalleryPhotoData__ - `request_id` · string · required - `success` · true - `data` · IndustryGalleryPhotoData[] · required - `id` · string · required - `industry_id` · string | null - `photo_url` · string · required - `public_url` · string | null: Public CDN URL of the original (no auth); null when not CDN-addressable. - `public_url_downloadable` · boolean | null: Whether browser fetch of public_url works (CORS). False for legacy R2 hosts (copy-only); null when public_url is null. - `photo_metadata` · PhotoMetadata | null - `created_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/industries/{industry_id}/photos/import-generated Import from generated photos Operation id: `import_industry_generated_api_v1_admin_industries__industry_id__photos_import_generated_post` Import selected generated photos (by job_id:index) into industry gallery. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ImportGeneratedBody - `generated_photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_list_IndustryGalleryPhotoData__ - `request_id` · string · required - `success` · true - `data` · IndustryGalleryPhotoData[] · required - `id` · string · required - `industry_id` · string | null - `photo_url` · string · required - `public_url` · string | null: Public CDN URL of the original (no auth); null when not CDN-addressable. - `public_url_downloadable` · boolean | null: Whether browser fetch of public_url works (CORS). False for legacy R2 hosts (copy-only); null when public_url is null. - `photo_metadata` · PhotoMetadata | null - `created_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/industries/{industry_id}/photos/search-unsplash Search Unsplash Operation id: `search_unsplash_industry_api_v1_admin_industries__industry_id__photos_search_unsplash_get` Search Unsplash for industry gallery workflows. Requires UNSPLASH_ACCESS_KEY. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `query` | query | string | yes | Search term | | `page` | query | integer | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_UnsplashSearchData_ - `request_id` · string · required - `success` · true - `data` · UnsplashSearchData · required: Unsplash search response (stub). - `items` · UnsplashSearchResultItem[] · required - `page` · integer - `total_pages` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/industries/{industry_id}/photos/template-defaults List template default photos per website slot Operation id: `list_industry_template_defaults_api_v1_admin_industries__industry_id__photos_template_defaults_get` Return each slot from the default website_photos_config with optional linked gallery photo. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__TemplateDefaultSlotData__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/industries/{industry_id}/photos/template-defaults Set or clear one template default photo slot Operation id: `put_industry_template_defaults_api_v1_admin_industries__industry_id__photos_template_defaults_put` Set one slot key to an industry gallery photo id, or null to clear it. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): PutTemplateDefaultsBody - `key` · string · required - `photo_id` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_str__TemplateDefaultSlotData__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/industries/{industry_id}/photos/upload Upload photo to industry gallery Operation id: `upload_industry_photo_api_v1_admin_industries__industry_id__photos_upload_post` Upload an industry gallery photo (multipart/form-data). Requires GCS bucket configured. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (multipart/form-data, required): Body_upload_industry_photo_api_v1_admin_industries__industry_id__photos_upload_post - `file` · string · required: Image file **Responses** - `201` Successful Response: SuccessResponse_IndustryGalleryPhotoData_ - `request_id` · string · required - `success` · true - `data` · IndustryGalleryPhotoData · required: Industry gallery photo for list response. - `id` · string · required - `industry_id` · string | null - `photo_url` · string · required - `public_url` · string | null: Public CDN URL of the original (no auth); null when not CDN-addressable. - `public_url_downloadable` · boolean | null: Whether browser fetch of public_url works (CORS). False for legacy R2 hosts (copy-only); null when public_url is null. - `photo_metadata` · PhotoMetadata | null - `created_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Platform admins 5 endpoints. HTML: https://developers.keystone.app/api/console/platform-admins/ ### POST /api/v1/admin/platform-admin-invites/{invite_id}/accept Accept a pending Keystone Admin invite for the authenticated user Operation id: `accept_platform_admin_invite_api_v1_admin_platform_admin_invites__invite_id__accept_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `invite_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PlatformAdminUserData_ - `request_id` · string · required - `success` · true - `data` · PlatformAdminUserData · required - `id` · string · required - `kind` · string · required - `first_name` · string - `last_name` · string - `email` · string (email) · required - `phone` · string | null - `role` · string · required - `status` · string · required - `delivery_status` · string | null - `joined_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/users List Keystone Admins and pending admin invites Operation id: `list_platform_admins_api_v1_admin_users_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PlatformAdminUserData__ - `request_id` · string · required - `success` · true - `data` · PlatformAdminUserData[] · required - `id` · string · required - `kind` · string · required - `first_name` · string - `last_name` · string - `email` · string (email) · required - `phone` · string | null - `role` · string · required - `status` · string · required - `delivery_status` · string | null - `joined_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/users Invite a Keystone Admin (pending invite + email) Operation id: `invite_platform_admin_api_v1_admin_users_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): PlatformAdminInviteBody - `email` · string (email) · required - `phone` · string | null **Responses** - `201` Successful Response: SuccessResponse_PlatformAdminUserData_ - `request_id` · string · required - `success` · true - `data` · PlatformAdminUserData · required - `id` · string · required - `kind` · string · required - `first_name` · string - `last_name` · string - `email` · string (email) · required - `phone` · string | null - `role` · string · required - `status` · string · required - `delivery_status` · string | null - `joined_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/users/{entry_id} Update a Keystone Admin or pending admin invite Operation id: `update_platform_admin_api_v1_admin_users__entry_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `entry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): PlatformAdminUpdateBody - `first_name` · string · required - `last_name` · string · required - `email` · string (email) · required - `phone` · string | null **Responses** - `200` Successful Response: SuccessResponse_PlatformAdminUserData_ - `request_id` · string · required - `success` · true - `data` · PlatformAdminUserData · required - `id` · string · required - `kind` · string · required - `first_name` · string - `last_name` · string - `email` · string (email) · required - `phone` · string | null - `role` · string · required - `status` · string · required - `delivery_status` · string | null - `joined_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/users/{entry_id} Remove pending admin invite or demote a Keystone Admin Operation id: `delete_platform_admin_api_v1_admin_users__entry_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `entry_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Pricing 7 endpoints. HTML: https://developers.keystone.app/api/console/pricing/ ### GET /api/v1/admin/pricing/books List price books (platform admin) Operation id: `list_price_books_api_v1_admin_pricing_books_get` Every book with its current version and the plans pointing at it. The live-subscriber count on each plan is the blast radius of an edit — and the only place a `price_book_id` is discoverable at all. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PriceBooksData_ - `request_id` · string · required - `success` · true - `data` · PriceBooksData · required - `price_books` · PriceBookData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/pricing/books/{book_id}/versions List a price book's versions (platform admin) Operation id: `list_price_book_versions_api_v1_admin_pricing_books__book_id__versions_get` Every version, newest first, with full contents — enough to diff any two, or to republish an old one as the newest, which is what a rollback is. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `book_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PriceBookVersionsData_ - `request_id` · string · required - `success` · true - `data` · PriceBookVersionsData · required - `versions` · PriceBookVersionData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/pricing/books/{book_id}/versions Publish a price book version (platform admin) Operation id: `create_price_book_version_api_v1_admin_pricing_books__book_id__versions_post` Publish the next version. Holds authorized after it commits price against the new version; every hold already open keeps the one it was opened under and settles at that one's prices, so an edit can never reprice work a customer has already started. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `book_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): CreateVersionBody - `rules` · object · required - `markup_multiplier` · string · required - `expected_version` · integer · required - `reason` · string · required **Responses** - `200` Successful Response: SuccessResponse_PriceBookVersionData_ - `request_id` · string · required - `success` · true - `data` · PriceBookVersionData · required: One publish: the complete snapshot that priced every usage row pinned to it. `credit_dollar_value` is the same in every version of a book — frozen for its life — and rides along so a version explains a charge on its own. - `version_id` · string (uuid) · required - `price_book_id` · string (uuid) · required - `version` · integer · required - `credit_dollar_value` · string · required - `markup_multiplier` · string · required - `rules` · object · required - `created_by` · string | null - `reason` · string - `created_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/pricing/dimensions List rating dimensions (platform admin) Operation id: `list_rating_dimensions_api_v1_admin_pricing_dimensions_get` The quantities the rater understands and the rate-sheet key each is priced under — a code constant served so the editor's field-level validation cannot drift from the code that does the pricing. It says nothing about which sheet is in force. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_RatingDimensionsData_ - `request_id` · string · required - `success` · true - `data` · RatingDimensionsData · required - `dimensions` · RatingDimensionData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/pricing/rates List vendor rate sheets (platform admin) Operation id: `list_rate_sheets_api_v1_admin_pricing_rates_get` Newest first, full tables inline — one call serves the list, a row's detail and the editor's starting point (the row payments marks `in_force`). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_RateSheetsData_ - `request_id` · string · required - `success` · true - `data` · RateSheetsData · required - `rate_sheets` · RateSheetData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/pricing/rates Schedule a vendor rate sheet (platform admin) Operation id: `create_rate_sheet_api_v1_admin_pricing_rates_post` Schedule a complete new sheet for a future instant. It is a draft until then: settle cannot see it, and it can still be edited. Only one may be pending at a time. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): RateSheetCreateBody - `name` · string · required - `rates` · object · required - `effective_from` · string (date-time) · required - `reason` · string · required **Responses** - `200` Successful Response: SuccessResponse_RateSheetData_ - `request_id` · string · required - `success` · true - `data` · RateSheetData · required: A vendor rate table and when it takes effect. `status` is computed by payments against its own clock, so the console picks the live row off the list rather than comparing timestamps itself. A draft is rewritten in place, so `created_at` is when it was opened and `updated_at` when it was last written. - `rates_id` · string (uuid) · required - `name` · string | null - `rates` · object · required - `effective_from` · string (date-time) | null - `status` · string · required - `created_by` · string | null - `reason` · string | null - `created_at` · string (date-time) | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/pricing/rates/{rates_id} Edit the scheduled rate sheet (platform admin) Operation id: `update_rate_sheet_api_v1_admin_pricing_rates__rates_id__put` Edit the draft in place: its instant may move later or earlier while it stays in the future, and moving it alone is a real edit. Once the instant passes the sheet is frozen — settled usage rows point at it — and this answers 409 `RATE_SHEET_IN_FORCE`. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `rates_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): RateSheetEditBody - `rates` · object · required - `effective_from` · string (date-time) · required - `reason` · string · required **Responses** - `200` Successful Response: SuccessResponse_RateSheetData_ - `request_id` · string · required - `success` · true - `data` · RateSheetData · required: A vendor rate table and when it takes effect. `status` is computed by payments against its own clock, so the console picks the live row off the list rather than comparing timestamps itself. A draft is rewritten in place, so `created_at` is when it was opened and `updated_at` when it was last written. - `rates_id` · string (uuid) · required - `name` · string | null - `rates` · object · required - `effective_from` · string (date-time) | null - `status` · string · required - `created_by` · string | null - `reason` · string | null - `created_at` · string (date-time) | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Promos 8 endpoints. HTML: https://developers.keystone.app/api/console/promos/ ### GET /api/v1/admin/promos/coupons List coupons (platform admin) Operation id: `list_coupons_api_v1_admin_promos_coupons_get` Every coupon, newest first. `include_deleted` adds the ones already deleted at Stripe — their rows are kept so past redemptions still resolve to the terms that were granted. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `include_deleted` | query | boolean | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CouponListData_ - `request_id` · string · required - `success` · true - `data` · CouponListData · required - `coupons` · CouponData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/promos/coupons Create a coupon (platform admin) Operation id: `create_coupon_api_v1_admin_promos_coupons_post` Create the discount's terms. The console names the plans it applies to (`applies_to_plan_names`, from `GET /promos/plans`); payments resolves each to the active plan's Stripe Product, which is why a name that is not an active priced plan comes back as a 400 rather than a silent unrestricted coupon. The restriction is immutable afterwards — a different set of plans is a different coupon. `idempotency_key` is the console's, generated once per form submission and passed to Stripe verbatim, so a retry after a lost response returns the first coupon instead of minting a second. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): CouponCreateBody - `name` · string · required - `percent_off` · string | null - `amount_off` · integer | null - `currency` · string | null - `duration` · string · required - `duration_in_months` · integer | null - `applies_to_plan_names` · string[] | null - `max_redemptions` · integer | null - `redeem_by` · string (date-time) | null - `idempotency_key` · string · required **Responses** - `200` Successful Response: SuccessResponse_CouponData_ - `request_id` · string · required - `success` · true - `data` · CouponData · required: A coupon as the admin screen renders it. `applies_to_plan_names` is `None` for "every plan" and `[]` for "restricted to products that are not any active plan of ours" — which only happens for a coupon created in the Stripe Dashboard. - `coupon_id` · string (uuid) · required - `name` · string · required - `percent_off` · string | null - `amount_off` · integer | null - `currency` · string | null - `duration` · string · required - `duration_in_months` · integer | null - `applies_to_plan_names` · string[] | null - `max_redemptions` · integer | null - `redeem_by` · string (date-time) | null - `valid` · boolean · required - `deleted_at` · string (date-time) | null - `created_by` · string | null - `times_redeemed` · integer - `created_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/promos/coupons/{coupon_id} One coupon with its codes (platform admin) Operation id: `get_coupon_api_v1_admin_promos_coupons__coupon_id__get` The detail screen: the coupon plus its code strings, each with its allowlist. A targeted code is issued once per business under the same string, so the entries **are** the allowlist; a public code has a single entry with no business. Every entry's business is enriched with its `company_name` here — payments stores only ids. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coupon_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CouponDetailData_ - `request_id` · string · required - `success` · true - `data` · CouponDetailData · required - `coupon_id` · string (uuid) · required - `name` · string · required - `percent_off` · string | null - `amount_off` · integer | null - `currency` · string | null - `duration` · string · required - `duration_in_months` · integer | null - `applies_to_plan_names` · string[] | null - `max_redemptions` · integer | null - `redeem_by` · string (date-time) | null - `valid` · boolean · required - `deleted_at` · string (date-time) | null - `created_by` · string | null - `times_redeemed` · integer - `created_at` · string (date-time) | null - `codes` · CodeGroupData[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/promos/coupons/{coupon_id} Delete a coupon (platform admin) Operation id: `delete_coupon_api_v1_admin_promos_coupons__coupon_id__delete` Delete at Stripe. New redemptions stop and every code under it goes inactive, but **discounts already granted keep running to their duration** — Stripe copies the terms onto the subscription at redemption, so deleting the coupon does not claw anything back. The mirror row survives, flagged deleted, so past redemptions still name their terms. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coupon_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CouponData_ - `request_id` · string · required - `success` · true - `data` · CouponData · required: A coupon as the admin screen renders it. `applies_to_plan_names` is `None` for "every plan" and `[]` for "restricted to products that are not any active plan of ours" — which only happens for a coupon created in the Stripe Dashboard. - `coupon_id` · string (uuid) · required - `name` · string · required - `percent_off` · string | null - `amount_off` · integer | null - `currency` · string | null - `duration` · string · required - `duration_in_months` · integer | null - `applies_to_plan_names` · string[] | null - `max_redemptions` · integer | null - `redeem_by` · string (date-time) | null - `valid` · boolean · required - `deleted_at` · string (date-time) | null - `created_by` · string | null - `times_redeemed` · integer - `created_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/promos/coupons/{coupon_id}/promotion-codes Issue a code, or add businesses to one (platform admin) Operation id: `create_promotion_codes_api_v1_admin_promos_coupons__coupon_id__promotion_codes_post` No `businesses` mints one public code; with them, one customer-restricted code **per business** sharing the string — which is also how a business is added to an existing code's allowlist. Unknown businesses are refused here, before payments is called: payments would create a Stripe Customer for the id to restrict the code to, and a Customer minted for a typo is permanent. The `company_name` sor holds is passed along to label that Customer, because payments has no other source for it. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coupon_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): PromotionCodesCreateBody - `code` · string · required - `businesses` · BusinessRefBody[] - `business_id` · string (uuid) · required - `expires_at` · string (date-time) | null - `max_redemptions` · integer | null - `first_time_transaction` · boolean - `minimum_amount` · integer - `listed_for_auto_apply` · boolean | null - `idempotency_key` · string · required **Responses** - `200` Successful Response: SuccessResponse_PromotionCodesData_ - `request_id` · string · required - `success` · true - `data` · PromotionCodesData · required - `promotion_codes` · PromotionCodeData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/promos/plans Plans a coupon can be restricted to (platform admin) Operation id: `list_promo_plans_api_v1_admin_promos_plans_get` The plan picker on the coupon form. This is payments' catalogue **without** the free plan: a plan with no Stripe price has no Product to carry a discount, so offering it would only produce a 400 on submit. The names here are exactly what `applies_to_plan_names` takes. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PromoPlansData_ - `request_id` · string · required - `success` · true - `data` · PromoPlansData · required - `plans` · PromoPlanData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/promos/promotion-codes/{promotion_code_id} Activate, deactivate, or list a promotion code (platform admin) Operation id: `update_promotion_code_api_v1_admin_promos_promotion_codes__promotion_code_id__patch` `active=false` is how a business leaves an allowlist — codes are never deleted at Stripe. `listed_for_auto_apply` controls whether the catalogue advertises the code in the customer's promo dropdown; an unlisted code still works when typed at Checkout. Stripe refuses to reactivate a code that expired, hit its cap, or whose coupon was deleted, and that refusal reaches the admin verbatim rather than as a generic failure. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `promotion_code_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): PromotionCodeUpdateBody - `active` · boolean | null - `listed_for_auto_apply` · boolean | null **Responses** - `200` Successful Response: SuccessResponse_PromotionCodeData_ - `request_id` · string · required - `success` · true - `data` · PromotionCodeData · required - `promotion_code_id` · string (uuid) · required - `coupon_id` · string (uuid) · required - `code` · string · required - `active` · boolean - `business_id` · string (uuid) | null - `company_name` · string | null - `expires_at` · string (date-time) | null - `max_redemptions` · integer | null - `first_time_transaction` · boolean - `minimum_amount` · integer - `listed_for_auto_apply` · boolean - `created_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/promos/redemptions Who redeemed what (platform admin) Operation id: `list_redemptions_api_v1_admin_promos_redemptions_get` Redemption reporting, newest first, filterable by business, coupon or code. A row is a Stripe Discount as payments mirrors it, so `ends_at` is null while the discount is still running. Each row's business is named here from sor's table. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | query | string (uuid) \| null | no | | | `coupon_id` | query | string (uuid) \| null | no | | | `promotion_code_id` | query | string (uuid) \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_RedemptionsData_ - `request_id` · string · required - `success` · true - `data` · RedemptionsData · required - `redemptions` · RedemptionData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Reviews 5 endpoints. HTML: https://developers.keystone.app/api/console/reviews/ ### GET /api/v1/admin/businesses/{business_id}/reviews List reviews Operation id: `list_reviews_api_v1_admin_businesses__business_id__reviews_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `cursor` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_AdminReview__ - `request_id` · string · required - `success` · true - `data` · AdminReview[] · required - `id` · string · required - `business_id` · string · required - `location_id` · string | null - `reviewer_name` · string · required - `reviewer_title` · string | null - `company` · string | null - `rating` · integer · required - `content_markdown` · string | null - `source` · string | null - `source_url` · string | null - `featured` · boolean - `reviewed_at` · string (date-time) · required - `photos` · PhotoData[] - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/reviews Create review Operation id: `create_review_api_v1_admin_businesses__business_id__reviews_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AdminReviewCreate - `reviewer_name` · string · required - `reviewer_title` · string | null - `company` · string | null - `rating` · integer · required - `content_markdown` · string | null - `source` · string · required - `source_url` · string | null - `featured` · boolean - `reviewed_at` · string (date-time) · required - `location_id` · string | null - `photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_AdminReview_ - `request_id` · string · required - `success` · true - `data` · AdminReview · required: Admin review for list/GET response. - `id` · string · required - `business_id` · string · required - `location_id` · string | null - `reviewer_name` · string · required - `reviewer_title` · string | null - `company` · string | null - `rating` · integer · required - `content_markdown` · string | null - `source` · string | null - `source_url` · string | null - `featured` · boolean - `reviewed_at` · string (date-time) · required - `photos` · PhotoData[] - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/reviews/{review_id} Get review details Operation id: `get_review_api_v1_admin_businesses__business_id__reviews__review_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_AdminReview_ - `request_id` · string · required - `success` · true - `data` · AdminReview · required: Admin review for list/GET response. - `id` · string · required - `business_id` · string · required - `location_id` · string | null - `reviewer_name` · string · required - `reviewer_title` · string | null - `company` · string | null - `rating` · integer · required - `content_markdown` · string | null - `source` · string | null - `source_url` · string | null - `featured` · boolean - `reviewed_at` · string (date-time) · required - `photos` · PhotoData[] - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/reviews/{review_id} Update review Operation id: `update_review_api_v1_admin_businesses__business_id__reviews__review_id__put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): AdminReviewUpdate - `reviewer_name` · string | null - `reviewer_title` · string | null - `company` · string | null - `rating` · integer | null - `content_markdown` · string | null - `source` · string | null - `source_url` · string | null - `featured` · boolean | null - `reviewed_at` · string (date-time) | null - `location_id` · string | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_AdminReview_ - `request_id` · string · required - `success` · true - `data` · AdminReview · required: Admin review for list/GET response. - `id` · string · required - `business_id` · string · required - `location_id` · string | null - `reviewer_name` · string · required - `reviewer_title` · string | null - `company` · string | null - `rating` · integer · required - `content_markdown` · string | null - `source` · string | null - `source_url` · string | null - `featured` · boolean - `reviewed_at` · string (date-time) · required - `photos` · PhotoData[] - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/reviews/{review_id} Delete review Operation id: `delete_review_api_v1_admin_businesses__business_id__reviews__review_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: SEO 6 endpoints. HTML: https://developers.keystone.app/api/console/seo/ ### GET /api/v1/admin/businesses/{business_id}/seo/actions The site's prioritized SEO action list Operation id: `list_actions_api_v1_admin_businesses__business_id__seo_actions_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ActionsData_ - `request_id` · string · required - `success` · true - `data` · ActionsData · required - `businessId` · string (uuid) · required - `websiteId` · string (uuid) · required - `actions` · ActionItem[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/seo/blog/overview Blog overview: search totals across every post, tracked keywords, per-post views Operation id: `get_blog_overview_api_v1_admin_businesses__business_id__seo_blog_overview_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BlogOverviewData_ - `request_id` · string · required - `success` · true - `data` · BlogOverviewData · required - `businessId` · string (uuid) · required - `websiteId` · string (uuid) · required - `generatedAt` · string (date-time) · required - `window` · Window · required - `totals` · BlogTotals · required - `keywords` · BlogKeywordRow[] · required - `posts` · object · required - `coverage` · Coverage · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/seo/blog/posts/{blog_post_id} Per-post performance: traffic, search, audit, speed and quality, with 30-day changes Operation id: `get_blog_post_performance_api_v1_admin_businesses__business_id__seo_blog_posts__blog_post_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `blog_post_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BlogPostPerformanceData_ - `request_id` · string · required - `success` · true - `data` · BlogPostPerformanceData · required - `blogPostId` · string (uuid) · required - `businessId` · string (uuid) · required - `websiteId` · string (uuid) · required - `slug` · string · required - `path` · string · required - `publishedOn` · string (date) | null - `generatedAt` · string (date-time) · required - `window` · Window · required - `isNew` · boolean · required - `sections` · PerfSection[] · required - `queries` · DrivingQuery[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/seo/scores Current SEO scores per category, with history Operation id: `get_scores_api_v1_admin_businesses__business_id__seo_scores_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ScoresData_ - `request_id` · string · required - `success` · true - `data` · ScoresData · required - `businessId` · string (uuid) · required - `websiteId` · string (uuid) · required - `generatedAt` · string (date-time) · required - `scores` · CategoryScore[] · required - `history` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/seo/site-health Site health: technical audit, content review, traffic and search, latest vs previous Operation id: `get_site_health_api_v1_admin_businesses__business_id__seo_site_health_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SiteHealthData_ - `request_id` · string · required - `success` · true - `data` · SiteHealthData · required - `businessId` · string (uuid) · required - `websiteId` · string (uuid) · required - `domain` · string · required - `generatedAt` · string (date-time) · required - `build` · BuildBlock | null - `reads` · ReadsBlock | null - `traffic` · TrafficBlock | null - `conversions` · ConversionsBlock | null - `search` · SearchBlock | null - `issuesTrend` · IssueTrendPoint[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/seo/portfolio Every site's current overall score — worst first Operation id: `portfolio_api_v1_admin_seo_portfolio_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PortfolioData_ - `request_id` · string · required - `success` · true - `data` · PortfolioData · required - `entries` · PortfolioEntry[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Service items 9 endpoints. HTML: https://developers.keystone.app/api/console/service-items/ ### GET /api/v1/admin/businesses/{business_id}/service-items Get all service items Operation id: `list_service_items_api_v1_admin_businesses__business_id__service_items_get` Get all service items for a business (ordered by display_order). Member services not included. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ServiceItemData__ - `request_id` · string · required - `success` · true - `data` · ServiceItemData[] · required - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/service-items Create service item Operation id: `create_service_item_api_v1_admin_businesses__business_id__service_items_post` Create a new service item. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ServiceItemCreateBody - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · number | string | null - `slug` · string | null - `photo_ids` · string[] - `is_featured` · boolean | null **Responses** - `201` Successful Response: SuccessResponse_ServiceItemData_ - `request_id` · string · required - `success` · true - `data` · ServiceItemData · required: Service item for list/GET response. `services` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-item member queries. - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/service-items/{item_id} Get service item details Operation id: `get_service_item_api_v1_admin_businesses__business_id__service_items__item_id__get` Get service item by id with member services. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ServiceItemData_ - `request_id` · string · required - `success` · true - `data` · ServiceItemData · required: Service item for list/GET response. `services` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-item member queries. - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/service-items/{item_id} Update service item Operation id: `update_service_item_api_v1_admin_businesses__business_id__service_items__item_id__put` Update service item details. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ServiceItemUpdateBody - `item_name` · string | null - `item_description` · string | null - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · number | string | null - `slug` · string | null - `photo_ids` · string[] | null - `is_featured` · boolean | null **Responses** - `200` Successful Response: SuccessResponse_ServiceItemData_ - `request_id` · string · required - `success` · true - `data` · ServiceItemData · required: Service item for list/GET response. `services` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-item member queries. - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/service-items/{item_id} Delete service item Operation id: `delete_service_item_api_v1_admin_businesses__business_id__service_items__item_id__delete` Soft-delete a service item. Member services survive (join rows cascade). 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/service-items/{item_id}/services Set service item member services Operation id: `set_item_services_api_v1_admin_businesses__business_id__service_items__item_id__services_put` Replace the service item's member services (and their in-item order). 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ServiceItemServicesBody - `ordered_service_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_ServiceItemData_ - `request_id` · string · required - `success` · true - `data` · ServiceItemData · required: Service item for list/GET response. `services` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-item member queries. - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/service-items/{item_id}/services/{service_id} Add service to service item Operation id: `add_service_to_item_api_v1_admin_businesses__business_id__service_items__item_id__services__service_id__post` Add a service to a service item (appended last). 404 if item not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `service_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/service-items/{item_id}/services/{service_id} Remove service from service item Operation id: `remove_service_from_item_api_v1_admin_businesses__business_id__service_items__item_id__services__service_id__delete` Remove a service from a service item. 404 if item or membership not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `service_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/service-items/reorder Reorder service items Operation id: `reorder_service_items_api_v1_admin_businesses__business_id__service_items_reorder_put` Reorder service items by ordered list of item ids. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ServiceItemReorderBody - `ordered_item_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_list_ServiceItemData__ - `request_id` · string · required - `success` · true - `data` · ServiceItemData[] · required - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Services 8 endpoints. HTML: https://developers.keystone.app/api/console/services/ ### GET /api/v1/admin/businesses/{business_id}/services Get all services Operation id: `list_services_api_v1_admin_businesses__business_id__services_get` Get all services for a business (ordered by display_order). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ServiceData__ - `request_id` · string · required - `success` · true - `data` · ServiceData[] · required - `id` · string · required - `business_id` · string · required - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `service_type` · string - `parent_service_id` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/services Create service Operation id: `create_service_api_v1_admin_businesses__business_id__services_post` Create a new service. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ServiceCreateBody - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `slug` · string | null - `photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_ServiceData_ - `request_id` · string · required - `success` · true - `data` · ServiceData · required: Service for list/GET response. - `id` · string · required - `business_id` · string · required - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `service_type` · string - `parent_service_id` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/services/{service_id} Get service details Operation id: `get_service_api_v1_admin_businesses__business_id__services__service_id__get` Get service by id. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `service_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ServiceData_ - `request_id` · string · required - `success` · true - `data` · ServiceData · required: Service for list/GET response. - `id` · string · required - `business_id` · string · required - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `service_type` · string - `parent_service_id` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/services/{service_id} Update service Operation id: `update_service_api_v1_admin_businesses__business_id__services__service_id__put` Update service details. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `service_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ServiceUpdateBody - `service_name` · string | null - `service_description` · string | null - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `slug` · string | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_ServiceData_ - `request_id` · string · required - `success` · true - `data` · ServiceData · required: Service for list/GET response. - `id` · string · required - `business_id` · string · required - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `service_type` · string - `parent_service_id` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/services/{service_id} Delete service Operation id: `delete_service_api_v1_admin_businesses__business_id__services__service_id__delete` Delete a service. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `service_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/services/{service_id}/service-items List service items for a service Operation id: `list_service_items_for_service_api_v1_admin_businesses__business_id__services__service_id__service_items_get` Service items belonging to this service, ordered by per-service display_order. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `service_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ServiceItemData__ - `request_id` · string · required - `success` · true - `data` · ServiceItemData[] · required - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/services/{service_id}/service-items Set service items for a service Operation id: `set_service_items_for_service_api_v1_admin_businesses__business_id__services__service_id__service_items_put` Replace the full set of service items for a service (ordered). Cross-tenant guard applied. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `service_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ServiceServiceItemsBody - `ordered_service_item_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_list_ServiceItemData__ - `request_id` · string · required - `success` · true - `data` · ServiceItemData[] · required - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/services/reorder Reorder services Operation id: `reorder_services_api_v1_admin_businesses__business_id__services_reorder_put` Reorder services by ordered list of service ids. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): ServiceReorderBody - `ordered_service_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_list_ServiceData__ - `request_id` · string · required - `success` · true - `data` · ServiceData[] · required - `id` · string · required - `business_id` · string · required - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `service_type` · string - `parent_service_id` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Social agent 2 endpoints. HTML: https://developers.keystone.app/api/console/social-agent/ ### GET /api/v1/businesses/{business_id}/social-agent/config Get effective social-agent config for a business Operation id: `get_config_api_v1_businesses__business_id__social_agent_config_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · SocialAgentConfigData · required - `business_id` · string · required - `enabled` · boolean - `auto_publish` · boolean - `frequency_type` · string - `instructions` · string - `meta` · SocialAgentConfigMeta · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/businesses/{business_id}/social-agent/config Update social-agent config for a business Operation id: `patch_config_api_v1_businesses__business_id__social_agent_config_patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): SocialAgentConfigPatchBody - `enabled` · boolean | null - `auto_publish` · boolean | null - `frequency_type` · string | null - `instructions` · string | null **Responses** - `200` Successful Response: SuccessResponse_SocialAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · SocialAgentConfigData · required - `business_id` · string · required - `enabled` · boolean - `auto_publish` · boolean - `frequency_type` · string - `instructions` · string - `meta` · SocialAgentConfigMeta · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Social integrations 11 endpoints. HTML: https://developers.keystone.app/api/console/social-integrations/ ### GET /api/v1/admin/businesses/{business_id}/social_integrations/meta Get Meta social integration status Operation id: `get_meta_integration_api_v1_admin_businesses__business_id__social_integrations_meta_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialIntegrationStatusData_ - `request_id` · string · required - `success` · true - `data` · SocialIntegrationStatusData · required - `business_id` · string · required - `provider` · string - `preferred_channel` · string - `status` · string · required - `channels` · SocialIntegrationChannelData[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/social_integrations/meta Disconnect Meta social integration Operation id: `disconnect_meta_integration_api_v1_admin_businesses__business_id__social_integrations_meta_delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `connection_channel` | query | string | yes | agency_business\|instagram_direct | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialIntegrationData_ - `request_id` · string · required - `success` · true - `data` · SocialIntegrationData · required - `id` · string | null - `business_id` · string · required - `provider` · string - `status` · string · required - `scopes` · string[] - `external_user_id` · string | null - `external_user_name` · string | null - `last_synced_at` · integer | null - `last_error_code` · string | null - `last_error_message` · string | null - `created_at` · integer | null - `updated_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/social_integrations/meta/agency Get Keystone agency Meta connection readiness Operation id: `get_meta_agency_status_api_v1_admin_businesses__business_id__social_integrations_meta_agency_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialAgencyConnectionData_ - `request_id` · string · required - `success` · true - `data` · SocialAgencyConnectionData · required - `provider` · string - `agency_business_id` · string | null - `status` · string · required - `connected` · boolean - `business_onboarding_completed` · boolean - `business_profiles_publishable` · boolean - `publish_blocking_reason` · string | null - `last_validated_at` · integer | null - `last_error_code` · string | null - `last_error_message` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/social_integrations/meta/agency/validate Validate Keystone agency Meta connection Operation id: `validate_meta_agency_api_v1_admin_businesses__business_id__social_integrations_meta_agency_validate_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialAgencyConnectionData_ - `request_id` · string · required - `success` · true - `data` · SocialAgencyConnectionData · required - `provider` · string - `agency_business_id` · string | null - `status` · string · required - `connected` · boolean - `business_onboarding_completed` · boolean - `business_profiles_publishable` · boolean - `publish_blocking_reason` · string | null - `last_validated_at` · integer | null - `last_error_code` · string | null - `last_error_message` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/social_integrations/meta/connect_url Get Meta OAuth connection URL Operation id: `get_meta_connect_url_api_v1_admin_businesses__business_id__social_integrations_meta_connect_url_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_MetaConnectUrlData_ - `request_id` · string · required - `success` · true - `data` · MetaConnectUrlData · required - `connect_url` · string · required - `expires_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/social_integrations/meta/instagram_direct/connect_url Get Instagram direct OAuth connection URL Operation id: `get_instagram_direct_connect_url_api_v1_admin_businesses__business_id__social_integrations_meta_instagram_direct_connect_url_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_MetaConnectUrlData_ - `request_id` · string · required - `success` · true - `data` · MetaConnectUrlData · required - `connect_url` · string · required - `expires_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/social_integrations/meta/instagram_direct/oauth/callback Complete Instagram direct OAuth callback Operation id: `instagram_direct_oauth_callback_api_v1_admin_businesses__business_id__social_integrations_meta_instagram_direct_oauth_callback_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `code` | query | string \| null | no | | | `state` | query | string \| null | no | | | `error` | query | string \| null | no | | | `error_description` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/social_integrations/meta/oauth/callback Complete Meta OAuth callback Operation id: `meta_oauth_callback_api_v1_admin_businesses__business_id__social_integrations_meta_oauth_callback_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `code` | query | string \| null | no | | | `state` | query | string \| null | no | | | `error` | query | string \| null | no | | | `error_description` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/social_integrations/meta/sync_profiles Sync Meta Facebook and Instagram profiles Operation id: `sync_meta_profiles_api_v1_admin_businesses__business_id__social_integrations_meta_sync_profiles_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `connection_channel` | query | string | no | all\|agency_business\|instagram_direct | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_MetaProfileSyncData_ - `request_id` · string · required - `success` · true - `data` · MetaProfileSyncData · required - `connection` · SocialIntegrationData · required - `profiles_synced` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/social_integrations/meta/instagram_direct/oauth/callback Complete Instagram direct OAuth callback Operation id: `instagram_direct_oauth_callback_static_api_v1_admin_social_integrations_meta_instagram_direct_oauth_callback_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `code` | query | string \| null | no | | | `state` | query | string \| null | no | | | `error` | query | string \| null | no | | | `error_description` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/social_integrations/meta/oauth/callback Complete Meta OAuth callback Operation id: `meta_oauth_callback_static_api_v1_admin_social_integrations_meta_oauth_callback_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `code` | query | string \| null | no | | | `state` | query | string \| null | no | | | `error` | query | string \| null | no | | | `error_description` | query | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Social posts 13 endpoints. HTML: https://developers.keystone.app/api/console/social-posts/ ### GET /api/v1/admin/businesses/{business_id}/social_posts List social posts for a business Operation id: `list_social_posts_api_v1_admin_businesses__business_id__social_posts_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string | no | Status filter: all\|draft\|queued\|published\|failed | | `platform` | query | string | no | Platform filter: all\|facebook\|instagram | | `search` | query | string \| null | no | Search post content | | `cursor` | query | string \| null | no | | | `direction` | query | string | no | Pagination direction: next\|prev | | `limit` | query | integer | no | | | `from_date` | query | string \| null | no | Optional scheduled/published lower bound as YYYY-MM-DD | | `to_date` | query | string \| null | no | Optional scheduled/published upper bound as YYYY-MM-DD | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_SocialPostListItemData__ - `request_id` · string · required - `success` · true - `data` · SocialPostListItemData[] · required - `id` · string · required - `business_id` · string · required - `content_preview` · string · required - `status` · "draft" | "queued" | "publishing" | "published" | "failed" · required - `origin` · string - `scheduled_at` · integer | null - `timezone` · string | null - `published_at` · integer | null - `photo_ids` · string[] - `media_status` · string | null - `thumbnail_photo_id` · string | null - `thumbnail_url` · string | null - `primary_profile_name` · string | null - `primary_profile_username` · string | null - `platforms` · SocialPostPlatformSummary[] - `engagement` · SocialEngagementData - `last_engagement_synced_at` · integer | null - `has_external_changes` · boolean - `external_change_kind` · string | null - `removed_externally` · boolean - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/social_posts Create a social post Operation id: `create_social_post_api_v1_admin_businesses__business_id__social_posts_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): SocialPostWriteBody - `content_markdown` · string · required - `link_url` · string | null - `photo_ids` · string[] - `target_profile_ids` · string[] - `target_formats` · SocialPostTargetFormatBody[] - `social_profile_id` · string · required - `format` · "facebook_feed" | "facebook_carousel" | "instagram_feed" | "instagram_carousel" · required - `publish_mode` · "draft" | "publish_now" | "schedule" - `scheduled_at` · integer | null - `timezone` · string | null - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null **Responses** - `201` Successful Response: SuccessResponse_SocialPostData_ - `request_id` · string · required - `success` · true - `data` · SocialPostData · required - `id` · string · required - `business_id` · string · required - `workflow_run_id` · string | null - `thread_id` · string | null - `content_markdown` · string · required - `link_url` · string | null - `photo_ids` · string[] - `media_status` · string | null - `status` · "draft" | "queued" | "publishing" | "published" | "failed" · required - `origin` · string - `scheduled_at` · integer | null - `timezone` · string | null - `published_at` · integer | null - `kairos_schedule_id` · string | null - `kairos_schedule_status` · string | null - `engagement` · SocialEngagementData - `last_engagement_synced_at` · integer | null - `media` · SocialPostPreviewMediaData[] - `targets` · SocialPostTargetData[] - `available_profiles` · SocialProfileData[] - `has_external_changes` · boolean - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/social_posts/{social_post_id} Get a social post Operation id: `get_social_post_api_v1_admin_businesses__business_id__social_posts__social_post_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `social_post_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialPostData_ - `request_id` · string · required - `success` · true - `data` · SocialPostData · required - `id` · string · required - `business_id` · string · required - `workflow_run_id` · string | null - `thread_id` · string | null - `content_markdown` · string · required - `link_url` · string | null - `photo_ids` · string[] - `media_status` · string | null - `status` · "draft" | "queued" | "publishing" | "published" | "failed" · required - `origin` · string - `scheduled_at` · integer | null - `timezone` · string | null - `published_at` · integer | null - `kairos_schedule_id` · string | null - `kairos_schedule_status` · string | null - `engagement` · SocialEngagementData - `last_engagement_synced_at` · integer | null - `media` · SocialPostPreviewMediaData[] - `targets` · SocialPostTargetData[] - `available_profiles` · SocialProfileData[] - `has_external_changes` · boolean - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/social_posts/{social_post_id} Update a social post Operation id: `update_social_post_api_v1_admin_businesses__business_id__social_posts__social_post_id__put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `social_post_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): SocialPostWriteBody - `content_markdown` · string · required - `link_url` · string | null - `photo_ids` · string[] - `target_profile_ids` · string[] - `target_formats` · SocialPostTargetFormatBody[] - `social_profile_id` · string · required - `format` · "facebook_feed" | "facebook_carousel" | "instagram_feed" | "instagram_carousel" · required - `publish_mode` · "draft" | "publish_now" | "schedule" - `scheduled_at` · integer | null - `timezone` · string | null - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null **Responses** - `200` Successful Response: SuccessResponse_SocialPostData_ - `request_id` · string · required - `success` · true - `data` · SocialPostData · required - `id` · string · required - `business_id` · string · required - `workflow_run_id` · string | null - `thread_id` · string | null - `content_markdown` · string · required - `link_url` · string | null - `photo_ids` · string[] - `media_status` · string | null - `status` · "draft" | "queued" | "publishing" | "published" | "failed" · required - `origin` · string - `scheduled_at` · integer | null - `timezone` · string | null - `published_at` · integer | null - `kairos_schedule_id` · string | null - `kairos_schedule_status` · string | null - `engagement` · SocialEngagementData - `last_engagement_synced_at` · integer | null - `media` · SocialPostPreviewMediaData[] - `targets` · SocialPostTargetData[] - `available_profiles` · SocialProfileData[] - `has_external_changes` · boolean - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/social_posts/{social_post_id} Delete a social post Operation id: `delete_social_post_api_v1_admin_businesses__business_id__social_posts__social_post_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `social_post_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/social_posts/{social_post_id}/preview Preview a social post Operation id: `preview_social_post_api_v1_admin_businesses__business_id__social_posts__social_post_id__preview_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `social_post_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): SocialPostPreviewBody - `surface` · "facebook_feed" | "facebook_carousel" | "instagram_feed" | "instagram_carousel" · required **Responses** - `200` Successful Response: SuccessResponse_SocialPostPreviewData_ - `request_id` · string · required - `success` · true - `data` · SocialPostPreviewData · required - `surface` · "facebook_feed" | "facebook_carousel" | "instagram_feed" | "instagram_carousel" · required - `profile_name` · string · required - `profile_username` · string | null - `profile_avatar_url` · string | null - `caption` · string · required - `link_url` · string | null - `photo_ids` · string[] - `media` · SocialPostPreviewMediaData[] - `engagement` · SocialEngagementData - `created_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/social_posts/{social_post_id}/publish Publish a social post immediately Operation id: `publish_social_post_api_v1_admin_businesses__business_id__social_posts__social_post_id__publish_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `social_post_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): SocialPostPublishBody - `target_profile_ids` · string[] - `target_formats` · SocialPostTargetFormatBody[] - `social_profile_id` · string · required - `format` · "facebook_feed" | "facebook_carousel" | "instagram_feed" | "instagram_carousel" · required **Responses** - `200` Successful Response: SuccessResponse_SocialPostPublishResult_ - `request_id` · string · required - `success` · true - `data` · SocialPostPublishResult · required - `social_post` · SocialPostData · required - `results` · SocialPostPublishTargetResult[] · required - `success` · boolean · required - `successful_count` · integer · required - `failed_count` · integer · required - `total_count` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/social_posts/{social_post_id}/sync Sync one post from Meta now (targeted re-read — catches IG edits) Operation id: `sync_social_post_api_v1_admin_businesses__business_id__social_posts__social_post_id__sync_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `social_post_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialFeedSyncData_ - `request_id` · string · required - `success` · true - `data` · SocialFeedSyncData · required: Result of a feed sync trigger (header / per-post / full). ``status`` reflects the trigger outcome (``ok`` / ``in_progress`` / ``not_due`` / ``no_profiles`` …). The console invalidates + refetches posts on ``ok``; ``detail`` carries per-scope counters for observability. - `status` · string · required - `detail` · object | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/social_posts/{social_post_id}/sync_engagement Refresh engagement metrics for a published social post Operation id: `sync_social_post_engagement_api_v1_admin_businesses__business_id__social_posts__social_post_id__sync_engagement_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `social_post_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialEngagementSyncData_ - `request_id` · string · required - `success` · true - `data` · SocialEngagementSyncData · required - `social_post` · SocialPostData · required - `synced` · integer · required - `failed` · integer · required - `skipped` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/social_posts/calendar_events List social post calendar events Operation id: `list_social_post_calendar_events_api_v1_admin_businesses__business_id__social_posts_calendar_events_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `from_date` | query | string \| null | no | Optional lower bound as YYYY-MM-DD | | `to_date` | query | string \| null | no | Optional upper bound as YYYY-MM-DD | | `platform` | query | string | no | Platform filter: all\|facebook\|instagram | | `status` | query | string | no | Status filter: all\|queued\|published\|failed | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_SocialPostCalendarEventData__ - `request_id` · string · required - `success` · true - `data` · SocialPostCalendarEventData[] · required - `id` · string · required - `post_id` · string · required - `target_id` · string · required - `social_post_id` · string · required - `social_post_target_id` · string · required - `social_profile_id` · string · required - `platform` · "facebook" | "instagram" · required - `status` · "pending" | "publishing" | "published" | "failed" | "draft" | "queued" | "publishing" | "published" | "failed" · required - `scheduled_at` · integer | null - `published_at` · integer | null - `title` · string · required - `content_preview` · string · required - `profile_name` · string | null - `profile_username` · string | null - `thumbnail_photo_id` · string | null - `thumbnail_url` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/social_posts/full_sync Admin: deep full sync of all feeds (re-reads every post, catches edits) Operation id: `full_sync_social_feeds_api_v1_admin_businesses__business_id__social_posts_full_sync_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialFeedSyncData_ - `request_id` · string · required - `success` · true - `data` · SocialFeedSyncData · required: Result of a feed sync trigger (header / per-post / full). ``status`` reflects the trigger outcome (``ok`` / ``in_progress`` / ``not_due`` / ``no_profiles`` …). The console invalidates + refetches posts on ``ok``; ``detail`` carries per-scope counters for observability. - `status` · string · required - `detail` · object | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/social_posts/generate Generate editable social post content Operation id: `generate_social_post_api_v1_admin_businesses__business_id__social_posts_generate_post` Generate a caption -- through the workflow when it is enabled. The workflow path persists the draft (and its photos) and returns it, so the console navigates into an existing post. The inline path returns a caption and nothing else, which is what it has always done. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `Idempotency-Key` | header | string \| null | no | | | `X-Workflow-Bypass` | header | string \| null | no | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): SocialPostGenerateBody - `prompt` · string | null - `target_profile_ids` · string[] - `platforms` · ("facebook" | "instagram")[] - `provider` · string | null **Responses** - `200` Successful Response: SuccessResponse_SocialPostGenerateData_ - `request_id` · string · required - `success` · true - `data` · SocialPostGenerateData · required - `content_markdown` · string · required - `media_briefs` · object[]: Photo briefs for the workflow worker to resolve against the media library, in display order. Empty unless social_media_briefs_enabled is on. The inline (non-workflow) callers return them but never act on them. - `metadata` · SocialPostGenerateMetadata · required - `generation_status` · string - `workflow_run_id` · string | null - `thread_id` · string | null - `social_post` · SocialPostData | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/social_posts/sync Sync all connected feeds now (incremental delta from Meta) Operation id: `sync_social_feeds_api_v1_admin_businesses__business_id__social_posts_sync_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialFeedSyncData_ - `request_id` · string · required - `success` · true - `data` · SocialFeedSyncData · required: Result of a feed sync trigger (header / per-post / full). ``status`` reflects the trigger outcome (``ok`` / ``in_progress`` / ``not_due`` / ``no_profiles`` …). The console invalidates + refetches posts on ``ok``; ``detail`` carries per-scope counters for observability. - `status` · string · required - `detail` · object | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Social profiles 3 endpoints. HTML: https://developers.keystone.app/api/console/social-profiles/ ### GET /api/v1/admin/businesses/{business_id}/social_profiles List social profiles for a business Operation id: `list_social_profiles_api_v1_admin_businesses__business_id__social_profiles_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `platform` | query | string | no | Platform filter: all\|facebook\|instagram | | `active` | query | boolean \| null | no | Optional active profile filter | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_SocialProfileData__ - `request_id` · string · required - `success` · true - `data` · SocialProfileData[] · required - `id` · string · required - `business_id` · string · required - `integration_connection_id` · string · required - `platform` · "facebook" | "instagram" · required - `profile_type` · "facebook_page" | "instagram_business_account" · required - `connection_channel` · "agency_business" | "instagram_direct" - `posting_identity` · "agency" | "business" - `external_id` · string · required - `parent_facebook_page_id` · string | null - `direct_fallback_profile_id` · string | null - `name` · string · required - `username` · string | null - `avatar_url` · string | null - `external_permalink_url` · string | null - `category` · string | null - `display_name_override` · string | null - `metadata` · object - `agency_publish_ready` · boolean - `token_expires_at` · integer | null - `token_last_validated_at` · integer | null - `token_last_refreshed_at` · integer | null - `token_last_error_code` · string | null - `token_last_error_message` · string | null - `active` · boolean · required - `sort_order` · integer · required - `created_at` · integer · required - `updated_at` · integer · required - `display_name` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/admin/businesses/{business_id}/social_profiles/{profile_id} Update local social profile settings Operation id: `update_social_profile_api_v1_admin_businesses__business_id__social_profiles__profile_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `profile_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): SocialProfileUpdateBody - `active` · boolean | null - `sort_order` · integer | null - `display_name_override` · string | null **Responses** - `200` Successful Response: SuccessResponse_SocialProfileData_ - `request_id` · string · required - `success` · true - `data` · SocialProfileData · required - `id` · string · required - `business_id` · string · required - `integration_connection_id` · string · required - `platform` · "facebook" | "instagram" · required - `profile_type` · "facebook_page" | "instagram_business_account" · required - `connection_channel` · "agency_business" | "instagram_direct" - `posting_identity` · "agency" | "business" - `external_id` · string · required - `parent_facebook_page_id` · string | null - `direct_fallback_profile_id` · string | null - `name` · string · required - `username` · string | null - `avatar_url` · string | null - `external_permalink_url` · string | null - `category` · string | null - `display_name_override` · string | null - `metadata` · object - `agency_publish_ready` · boolean - `token_expires_at` · integer | null - `token_last_validated_at` · integer | null - `token_last_refreshed_at` · integer | null - `token_last_error_code` · string | null - `token_last_error_message` · string | null - `active` · boolean · required - `sort_order` · integer · required - `created_at` · integer · required - `updated_at` · integer · required - `display_name` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/social_profiles/{profile_id}/refresh_token Refresh a social profile publish token when supported Operation id: `refresh_social_profile_token_api_v1_admin_businesses__business_id__social_profiles__profile_id__refresh_token_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `profile_id` | path | string (uuid) | yes | | | `required_valid_until` | query | integer \| null | no | Optional epoch time the token must remain valid through | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialProfileTokenRefreshData_ - `request_id` · string · required - `success` · true - `data` · SocialProfileTokenRefreshData · required - `social_profile_id` · string · required - `platform` · "facebook" | "instagram" · required - `connection_channel` · "agency_business" | "instagram_direct" · required - `refresh_supported` · boolean · required - `refreshed` · boolean - `reconnect_required` · boolean - `token_expires_at` · integer | null - `last_refreshed_at` · integer | null - `message` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Team 6 endpoints. HTML: https://developers.keystone.app/api/console/team/ ### GET /api/v1/admin/businesses/{business_id}/team_members Get all team members Operation id: `list_team_members_api_v1_admin_businesses__business_id__team_members_get` Get all team members for a business (ordered by display_order). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_TeamMemberData__ - `request_id` · string · required - `success` · true - `data` · TeamMemberData[] · required - `id` · string · required - `business_id` · string · required - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/team_members Create team member Operation id: `create_team_member_api_v1_admin_businesses__business_id__team_members_post` Create a new team member. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): TeamMemberCreateBody - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_TeamMemberData_ - `request_id` · string · required - `success` · true - `data` · TeamMemberData · required: Team member for list/GET response. - `id` · string · required - `business_id` · string · required - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/team_members/{team_member_id} Get team member details Operation id: `get_team_member_api_v1_admin_businesses__business_id__team_members__team_member_id__get` Get team member by id. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `team_member_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_TeamMemberData_ - `request_id` · string · required - `success` · true - `data` · TeamMemberData · required: Team member for list/GET response. - `id` · string · required - `business_id` · string · required - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/team_members/{team_member_id} Update team member Operation id: `update_team_member_api_v1_admin_businesses__business_id__team_members__team_member_id__put` Update team member details. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `team_member_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): TeamMemberUpdateBody - `full_name` · string | null - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_TeamMemberData_ - `request_id` · string · required - `success` · true - `data` · TeamMemberData · required: Team member for list/GET response. - `id` · string · required - `business_id` · string · required - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/team_members/{team_member_id} Delete team member Operation id: `delete_team_member_api_v1_admin_businesses__business_id__team_members__team_member_id__delete` Delete a team member. 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `team_member_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/team_members/reorder Reorder team members Operation id: `reorder_team_members_api_v1_admin_businesses__business_id__team_members_reorder_put` Reorder team members by ordered list of team member ids. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): TeamMemberReorderBody - `ordered_team_member_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_list_TeamMemberData__ - `request_id` · string · required - `success` · true - `data` · TeamMemberData[] · required - `id` · string · required - `business_id` · string · required - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Voice calls 2 endpoints. HTML: https://developers.keystone.app/api/console/voice-calls/ ### GET /api/v1/admin/businesses/{business_id}/voice-calls/{call_id} Front Desk call detail (transcript + analysis) Operation id: `get_voice_call_api_v1_admin_businesses__business_id__voice_calls__call_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `call_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_VoiceCallDetailData_ - `request_id` · string · required - `success` · true - `data` · VoiceCallDetailData · required: One call's full record for the console thread widget (S3): the message row carries only the summary + ``ai_metadata.voice_call_id``; transcript and analysis are fetched on expand through this shape. No vendor URLs — playback streams through the recording proxy (design doc P28). - `id` · string · required - `business_id` · string · required - `contact_id` · string | null - `provider` · string · required - `from_number` · string | null - `to_number` · string | null - `started_at` · string | null - `ended_at` · string | null - `duration_ms` · integer | null - `disconnection_reason` · string | null - `transcript` · string | null - `summary` · string | null - `captured_name` · string | null - `captured_email` · string | null - `custom_analysis` · object | null - `has_recording` · boolean · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/voice-calls/{call_id}/recording Stream the call recording (audit-logged) Operation id: `stream_voice_call_recording_api_v1_admin_businesses__business_id__voice_calls__call_id__recording_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `call_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Webchat 2 endpoints. HTML: https://developers.keystone.app/api/console/webchat/ ### GET /api/v1/businesses/{business_id}/webchat/config Get webchat config Operation id: `get_webchat_config_api_v1_businesses__business_id__webchat_config_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/businesses/{business_id}/webchat/config Update webchat greeting Operation id: `put_webchat_config_api_v1_businesses__business_id__webchat_config_put` Updates greeting only. Toggle chat on/off via PATCH /website-agent/config (webchat_enabled). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): WebchatConfigBody - `greeting` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Website 19 endpoints. HTML: https://developers.keystone.app/api/console/website/ ### GET /api/v1/admin/businesses/{business_id}/website Get website for business Operation id: `get_website_api_v1_admin_businesses__business_id__website_get` Always 200. Returns optional website payload (null if not created). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteGetResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteGetResponse · required: Response for GET /businesses/{id}/website. - `website` · WebsiteResponse | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website Create website for business Operation id: `create_website_api_v1_admin_businesses__business_id__website_post` Create website + API key for the business, trigger provisioning, and return website only. Takes no input beyond ``business_id``: the generator builds from the business's own backend content, so there is no design prompt to supply. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, optional): WebsiteCreateBody | null **Responses** - `201` Successful Response: SuccessResponse_WebsiteCreateResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteCreateResponse · required: Response for create website endpoint: website only (API key is not returned). - `website` · WebsiteResponse · required: Website fields for admin display. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/website-check/artifacts/{artifact_id} A screenshot the site agent's browser check took Operation id: `get_website_check_artifact_api_v1_admin_businesses__business_id__website_check_artifacts__artifact_id__get` The image bytes; 404 for a screenshot of another business, or none. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `artifact_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/website-mod/requirements/{file_id} Download a document attached for the site agent Operation id: `download_website_mod_requirement_api_v1_admin_businesses__business_id__website_mod_requirements__file_id__get` The document as the owner attached it; 404 for another business's. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `file_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/requirements/upload Upload a document for the site agent (website editor chats) Operation id: `upload_website_mod_requirements_api_v1_admin_businesses__business_id__website_mod_requirements_upload_post` Store a document the owner attached for the site agent. With the private bucket: a PDF, Word, Excel, PowerPoint or text file, validated and converted at once (the answer lists the derived files and any ``warnings``); without it, text files only, as before. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (multipart/form-data, required): Body_upload_website_mod_requirements_api_v1_admin_businesses__business_id__website_mod_requirements_upload_post - `file` · string · required **Responses** - `200` Successful Response: SuccessResponse_WebsiteAgentDocumentUpload_ - `request_id` · string · required - `success` · true - `data` · WebsiteAgentDocumentUpload · required: The answer to a document upload for the site agent. - `file_id` · string · required - `filename` · string · required - `content_type` · string | null - `kind` · string | null - `size` · integer | null - `derived` · string[] - `warnings` · string[] - `pages` · integer | null - `sheets` · integer | null - `slides` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/website/api-key Reveal the website's public API key (live credential) Operation id: `reveal_website_api_key_api_v1_admin_businesses__business_id__website_api_key_get` Return the site's live public API key, in the clear. 404 while the feature flag is off; 404 for an unknown business or a business with no website; 409 when the site has no readable key yet. The key returned is the one the running site uses — revealing never rotates. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteApiKeyRevealData_ - `request_id` · string · required - `success` · true - `data` · WebsiteApiKeyRevealData · required: The site's live public API key, in the clear (reveal endpoint only). Deliberately ONE field. This is the only response body in sor that carries a working credential, so it carries nothing else: every extra field would be another value riding along in a payload that must not be stored, logged, or cached anywhere. ``key_prefix`` in particular is omitted — it is literally the key's own first eight characters, and a caller holding the whole key has no use for a slice of it. The value is the SAME string the site's ``.env`` carries (decrypted from ``business_api_keys.key_encrypted``); revealing never mints a new key, so a reveal cannot invalidate a running site. - `api_key` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/website/collaborators List who has access to the business's site repo Operation id: `list_website_collaborators_api_v1_admin_businesses__business_id__website_collaborators_get` Direct collaborators (``status="active"``) merged with pending invitations (``status="invited"``). Direct affiliation only: org-inherited members (Keystone staff, the App) are asked out of the list at GitHub. 404 while the flag is off, for an unknown business, and for a business with no site repo yet. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteCollaboratorsData_ - `request_id` · string · required - `success` · true - `data` · WebsiteCollaboratorsData · required: Combined view: active direct collaborators + pending invitations. - `repo_full_name` · string · required - `collaborators` · WebsiteCollaboratorRow[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website/collaborators Invite a GitHub username to the business's site repo (push) Operation id: `invite_website_collaborator_api_v1_admin_businesses__business_id__website_collaborators_post` Invite at push. Idempotent-friendly: an already-invited or already-active username answers 200 with ``already=true`` and a sentence saying so — repeats are clean answers, not errors. 422 for a blank or unknown username; 503 ``GITHUB_APP_PERMISSION_MISSING`` when the App lacks the Administration permission. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): WebsiteCollaboratorInviteBody - `username` · string · required **Responses** - `200` Successful Response: SuccessResponse_WebsiteCollaboratorInviteData_ - `request_id` · string · required - `success` · true - `data` · WebsiteCollaboratorInviteData · required: Outcome of an invite — including the two idempotent "nothing to do" shapes, which are clean answers rather than errors: - fresh invitation: ``status="invited"``, ``already=False`` - already invited: ``status="invited"``, ``already=True`` (their pending invitation, untouched) - already active: ``status="active"``, ``already=True`` (GitHub answered 204: access exists or was granted directly, no invitation involved; ``permission`` is None because their actual level was not re-read) - `username` · string · required - `status` · "active" | "invited" · required - `already` · boolean · required - `permission` · "admin" | "maintain" | "push" | "triage" | "pull" | null - `invitation_id` · integer | null - `message` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/website/collaborators/{username} Remove a collaborator or cancel their pending invitation Operation id: `remove_website_collaborator_api_v1_admin_businesses__business_id__website_collaborators__username__delete` One endpoint for both shapes of "take their access away": sor cancels the pending invitation if that is what exists, removes the active direct collaborator otherwise. 404 when the username has neither. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `username` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteCollaboratorRemoveData_ - `request_id` · string · required - `success` · true - `data` · WebsiteCollaboratorRemoveData · required: Outcome of a remove: which of the two GitHub acts sor performed — cancelling a pending invitation or removing an active collaborator. - `username` · string · required - `removed` · "invitation" | "collaborator" · required - `message` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/website/commits List the website's change history (History tab) Operation id: `list_website_commits_api_v1_admin_businesses__business_id__website_commits_get` The conversation's changes as a git log, newest first. 404 while the preview flag is off, and for a business with no website row. A website with no repository yet answers 200 with the honest-empty shape. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteCommitsResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteCommitsResponse · required: The History tab's read. A website row that has no repository yet answers honestly empty: ``repo_full_name`` and ``default_branch`` are None and ``commits`` is ``[]`` — the console shows "no repository yet". A GitHub failure, by contrast, is surfaced as an error, never as this empty shape. ``launched_at``/``confirm_text`` are the preview's confirm moment and the customer's confirming message — the console renders a "Building your full website…" narration row and a "Your site went live" divider at that point in the timeline. Both None for a site that never went through the preview flow (or has not confirmed yet). - `repo_full_name` · string | null - `default_branch` · string | null - `launched_at` · string | null - `confirm_text` · string | null - `commits` · WebsiteCommitItem[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/admin/businesses/{business_id}/website/photos Update website photo mappings Operation id: `update_website_photos_api_v1_admin_businesses__business_id__website_photos_put` Update website photo selections (partial). Only slot IDs present in the active config are applied. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): WebsitePhotosUpdate **Responses** - `200` Successful Response: SuccessResponse_WebsiteResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteResponse · required: Website fields for admin display. - `id` · string (uuid) · required - `business_id` · string (uuid) · required - `name` · string · required - `slug` · string · required - `domain` · string · required - `preview_url` · string | null - `worker_url` · string | null - `custom_domain` · string | null - `source_domain` · string | null - `status` · string · required - `custom_prompt` · string | null - `provisioning_error` · string | null - `provisioning_logs` · ProvisioningLogEntry[] | null - `github_repo_id` · integer | null - `website_photos` · WebsitePhotos | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/websites/{website_id}/custom-domain Get custom domain state Operation id: `get_custom_domain_api_v1_admin_websites__website_id__custom_domain_get` Return current custom_domain, worker_name (repo_name), and preview_url when set; otherwise success with custom_domain: null. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `website_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CustomDomainGetResponse_ - `request_id` · string · required - `success` · true - `data` · CustomDomainGetResponse · required: Response for GET /websites/{website_id}/custom-domain. When custom_domain is set, includes worker_name and preview_url. - `success` · boolean - `custom_domain` · string | null - `source_domain` · string | null - `worker_name` · string | null - `preview_url` · string | null - `entri_enabled` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/websites/{website_id}/custom-domain Create or attach custom domain Operation id: `create_custom_domain_api_v1_admin_websites__website_id__custom_domain_post` Set custom domain for a website. Normalizes apex to www, creates Cloudflare custom hostname if needed, returns DNS instructions. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `website_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): CustomDomainCreateRequest - `domain` · string · required **Responses** - `200` Successful Response: SuccessResponse_CustomDomainCreateResponse_ - `request_id` · string · required - `success` · true - `data` · CustomDomainCreateResponse · required: Response for POST /websites/{website_id}/custom-domain. - `success` · boolean - `custom_domain` · string · required - `dns_instructions` · DnsInstructions · required - `converted_from` · string | null - `message` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/websites/{website_id}/custom-domain Remove custom domain Operation id: `delete_custom_domain_api_v1_admin_websites__website_id__custom_domain_delete` Remove custom domain: delete in Cloudflare and clear website.custom_domain. 404 if none configured. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `website_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CustomDomainDeleteResponse_ - `request_id` · string · required - `success` · true - `data` · CustomDomainDeleteResponse · required: Response for DELETE /websites/{website_id}/custom-domain. - `message` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/websites/{website_id}/custom-domain-status Get custom domain status Operation id: `get_custom_domain_status_api_v1_admin_websites__website_id__custom_domain_status_get` Return Cloudflare status, ssl_status, validation_records, and dns_instructions when custom_domain is set; else { status: 'none' }. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `website_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CustomDomainStatusResponse_ - `request_id` · string · required - `success` · true - `data` · CustomDomainStatusResponse · required: Response for GET /websites/{website_id}/custom-domain-status. When status is 'none', only success and status; else full CF details + dns_instructions. - `success` · boolean - `status` · string · required - `domain` · string | null - `ssl_status` · string | null - `validation_errors` · any[] | null - `validation_records` · object[] | null - `dns_instructions` · DnsInstructions | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/websites/{website_id}/entri-setup Get Entri setup (token + dnsRecords) for automated DNS Operation id: `get_entri_setup_api_v1_admin_websites__website_id__entri_setup_get` Mint a fresh Entri token and build the dnsRecords for entri.showEntri(). Dedicated endpoint (not folded into create/status) so a fresh short-lived token is minted each time the user opens the modal. 409 if no custom domain is configured yet. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `website_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_EntriSetupResponse_ - `request_id` · string · required - `success` · true - `data` · EntriSetupResponse · required: Response for GET /websites/{website_id}/entri-setup: fresh token + records for entri.showEntri(). - `success` · boolean - `application_id` · string · required - `token` · string · required - `prefilled_domain` · string · required - `dns_records` · EntriDnsRecord[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/websites/{website_id}/provisioning-stream Stream provisioning logs and status for a website (SSE) Operation id: `stream_provisioning_api_v1_admin_websites__website_id__provisioning_stream_get` Server-Sent Events stream of provisioning logs/status for a website. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `website_id` | path | string (uuid) | yes | | | `after_seq` | query | integer \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: string - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/websites/{website_id}/reconcile-custom-domain Reconcile orphaned custom domain Operation id: `reconcile_custom_domain_api_v1_admin_websites__website_id__reconcile_custom_domain_post` Link an orphaned domain (in Cloudflare but not in DB) to this website. 404 if not in CF; 409 if linked to another website. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `website_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): CustomDomainCreateRequest - `domain` · string · required **Responses** - `200` Successful Response: SuccessResponse_CustomDomainReconcileResponse_ - `request_id` · string · required - `success` · true - `data` · CustomDomainReconcileResponse · required: Response for POST /websites/{website_id}/reconcile-custom-domain. - `success` · boolean - `message` · string · required - `custom_domain` · string · required - `cloudflare_status` · CloudflareStatusInfo · required: Cloudflare custom hostname status (status, ssl_status, validation_errors). - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/websites/{website_id}/reprovision Re-provision a failed website Operation id: `reprovision_website_api_v1_admin_websites__website_id__reprovision_post` Reset a failed website and re-run its build in the background. 404 if the website does not exist, 409 if it is already active or currently provisioning. On success the error is cleared and a fresh build is kicked off. Without a website preview that is the classic path the create endpoint uses (status reset to ``provisioning``); a website preview's failed full build is retried as that build, seeded from the confirmed draft — 409 for a draft not confirmed yet (see ``website_svc.reprovision_website``) — and the answer waits (bounded) for the workflow start it makes, as the preview routes do. A plain retry: it re-runs the same fixed brief against the business's current backend content. The body is accepted but ignored (see ``WebsiteReprovisionBody``). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `website_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, optional): WebsiteReprovisionBody | null **Responses** - `200` Successful Response: SuccessResponse_WebsiteReprovisionResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteReprovisionResponse · required: Response for reprovision endpoint: the website after its status is reset to provisioning. - `website` · WebsiteResponse · required: Website fields for admin display. - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Website agent 3 endpoints. HTML: https://developers.keystone.app/api/console/website-agent/ ### GET /api/v1/businesses/{business_id}/website-agent/config Get effective website-agent settings for a business Operation id: `get_config_api_v1_businesses__business_id__website_agent_config_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · WebsiteAgentConfigData · required: Website agent + live webchat gate (webchat_enabled → webchat_configs). - `business_id` · string · required - `enabled` · boolean · required - `instructions` · string - `webchat_enabled` · boolean - `editor_model_id` · string | null - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/businesses/{business_id}/website-agent/config Update website-agent settings for a business Operation id: `patch_config_api_v1_businesses__business_id__website_agent_config_patch` Partial diff. ``enabled`` / ``instructions`` persist as a new agent-settings version; ``webchat_enabled`` upserts webchat_configs in the same transaction (gates the live widget). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): WebsiteAgentConfigPatchBody - `enabled` · boolean | null - `instructions` · string | null - `webchat_enabled` · boolean | null - `editor_model_id` · string | null **Responses** - `200` Successful Response: SuccessResponse_WebsiteAgentConfigData_ - `request_id` · string · required - `success` · true - `data` · WebsiteAgentConfigData · required: Website agent + live webchat gate (webchat_enabled → webchat_configs). - `business_id` · string · required - `enabled` · boolean · required - `instructions` · string - `webchat_enabled` · boolean - `editor_model_id` · string | null - `version` · integer | null - `updated_at` · string (date-time) | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/businesses/{business_id}/website-agent/models The models the website editor offers, and its defaults Operation id: `list_models_api_v1_businesses__business_id__website_agent_models_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteAgentModelsData_ - `request_id` · string · required - `success` · true - `data` · WebsiteAgentModelsData · required: The editor's model picker: what it offers, and what it starts on. - `models` · WebsiteAgentModelOption[] · required - `default_model_id` · string · required - `business_default_model_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Website modifications 16 endpoints. HTML: https://developers.keystone.app/api/console/website-mod/ ### GET /api/v1/admin/businesses/{business_id}/website-mod Read the business's website-mod thread, pending change and preview Operation id: `get_website_mod_state_api_v1_admin_businesses__business_id__website_mod_get` The Changes tab's whole read, in one call. ``publishable`` answers the only question the tab's button needs: there is a change awaiting approval, it belongs to this console thread, and both the run id and the approval bundle ref needed to approve it are known. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteModStateResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteModStateResponse · required: The Changes tab's whole read: the chat, the pending change, the preview. ``messages`` is the control plane's thread-detail ``items`` passed straight through — sor does not reinterpret widget blocks, the console already maps them (it reads the same shape from the control plane on the inbox surface). ``preview_url`` is the version preview of ``preview_head_sha``, the newest commit on the modification branch; both are None until the branch's preview build lands a Cloudflare version. - `thread_id` · string · required - `website_id` · string · required - `thread_status` · string | null - `thread_failure_code` · string | null - `thread_failure_message` · string | null - `merge_conflict` · WebsiteModMergeConflict | null - `messages` · object[] - `pending_change` · WebsiteModPendingChange | null - `approval_state` · string | null - `approval_bundle_ref` · string | null - `workflow_run_id` · string | null - `preview_url` · string | null - `preview_head_sha` · string | null - `preview_frame_sha` · string | null - `preview_kind` · string | null - `session_enabled` · boolean - `publishable` · boolean - `progress` · WebsiteModProgress | null - `pending_confirmation` · WebsiteModConfirmation | null - `data_changed_at` · number | null - `pending_data_changes` · string[] - `capabilities` · EditorCapabilities - `browser_check` · BrowserCheckView | null - `versions` · WebsiteModVersion[] - `undone_versions` · WebsiteModVersion[] - `queued_requests` · EditorQueuedRequest[] - `queue` · EditorQueueState | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/cancel Cancel the pending website change Operation id: `cancel_website_mod_change_api_v1_admin_businesses__business_id__website_mod_cancel_post` Drive the run to completion, THEN clear pending, close the PR, unlock. Order is the fix for a live bug: cleanup-first left the parked Temporal workflow squatting on the thread's workflow id whenever the reject could not be delivered (a change discarded mid-planning has no approval bundle to reject), and the next submit — with every sor gate now green — hit the engine's raw TEMPORAL_START_FAILED. The unwind now runs FIRST and fails LOUDLY: on failure nothing has been cleared, the pending change is still visible, and the cancel can simply be retried. Only once a completion lever is armed (reject delivered, workflow already gone, or the run tombstoned so the plan surface terminates it) does the local cleanup run — idempotent with the engine's own reject-path cleanup. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, optional): WebsiteModCancelBody | null - `comment` · string | null **Responses** - `200` Successful Response: SuccessResponse_WebsiteModCancelResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteModCancelResponse · required: Cancel result, straight from ``cancel_pending_website_mod``. - `cancelled` · boolean · required - `thread_id` · string · required - `cleared_pending` · boolean · required - `released_lock` · boolean · required - `closed_pull_request_numbers` · integer[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/changes Submit a website change request from the console Operation id: `submit_website_mod_change_api_v1_admin_businesses__business_id__website_mod_changes_post` 202: the request is recorded on the thread and a run is started on it. The run then drives the unchanged pipeline — plan-and-open-pr, preview polling, the approval gate this tab's Publish answers, merge-deploy — so the console never has to reimplement any stage of it. Poll the GET for the pending change and the preview URL. ``text`` still rides along as the run's ``user_message`` input (the workflow reads it); it is ALSO written to the thread, because an input is not a record — before that, a reload lost the operator's own words. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): WebsiteModChangeBody - `text` · string | null · required - `client_message_id` · string | null - `model_id` · string | null - `attachments` · WebsiteAgentAttachmentRef[] - `kind` · "photo" | "document" · required - `id` · string · required - `delivery` · "now" | "queue" | null **Responses** - `202` Successful Response: SuccessResponse_WebsiteModChangeResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteModChangeResponse · required: 202 body for a submitted change: what to poll, and where. - `thread_id` · string · required - `workflow_run_id` · string | null - `status` · string · required - `steered` · boolean - `queued` · boolean - `queued_request_id` · string | null - `note_refused` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/check/override Let a change its browser check holds back be published (Keystone staff) Operation id: `override_website_mod_check_api_v1_admin_businesses__business_id__website_mod_check_override_post` Keystone staff only: the reason and what the check had found are kept in ``website_check_overrides``. 409 when nothing is held back. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): BrowserCheckOverrideBody - `reason` · string · required **Responses** - `200` Successful Response: SuccessResponse_BrowserCheckView_ - `request_id` · string · required - `success` · true - `data` · BrowserCheckView · required: The newest browser check of what Publish (website-mod) or Finish (preview) would ship, and whether it holds that back. - `id` · string · required - `status` · string · required - `blocked` · boolean · required - `problems` · string[] - `pages` · BrowserCheckPage[] - `checked_at` · string (date-time) | null - `overridden` · boolean - `override_reason` · string | null - `can_recheck` · boolean - `rechecking` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/check/recheck Look at the pending change's pages in a browser again Operation id: `recheck_website_mod_check_api_v1_admin_businesses__business_id__website_mod_check_recheck_post` 202: the session pod looks again, without the site agent, at the pages a check that never reached the browser was to look at. The state read shows ``rechecking`` until the new check lands. 409 for a check a look cannot settle (problems the agent saw), or while the agent works. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `202` Successful Response: SuccessResponse_BrowserCheckView_ - `request_id` · string · required - `success` · true - `data` · BrowserCheckView · required: The newest browser check of what Publish (website-mod) or Finish (preview) would ship, and whether it holds that back. - `id` · string · required - `status` · string · required - `blocked` · boolean · required - `problems` · string[] - `pages` · BrowserCheckPage[] - `checked_at` · string (date-time) | null - `overridden` · boolean - `override_reason` · string | null - `can_recheck` · boolean - `rechecking` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/confirmations/{confirmation_id} Answer the site agent's question (continue or stop) Operation id: `answer_website_mod_confirmation_api_v1_admin_businesses__business_id__website_mod_confirmations__confirmation_id__post` 202: the answer is on the thread and the parked run resumes with it. Continue lets the agent carry out the plan it described; Stop reverts the work so far. Either way the change goes back to ``planning`` until the agent reports again. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `confirmation_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): WebsiteModConfirmationBody - `decision` · "continue" | "stop" · required - `text` · string | null - `client_message_id` · string | null - `attachments` · WebsiteAgentAttachmentRef[] - `kind` · "photo" | "document" · required - `id` · string · required **Responses** - `202` Successful Response: SuccessResponse_WebsiteModChangeResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteModChangeResponse · required: 202 body for a submitted change: what to poll, and where. - `thread_id` · string · required - `workflow_run_id` · string | null - `status` · string · required - `steered` · boolean - `queued` · boolean - `queued_request_id` · string | null - `note_refused` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/merge-conflict/decline Decline rebuilding a change the site's direct edits refused Operation id: `decline_website_mod_rebuild_api_v1_admin_businesses__business_id__website_mod_merge_conflict_decline_post` The owner's "no" to rebuilding a Publish the site's own, direct changes refused: the change is let go for good, and the editor stops calling it a failure (dev 2026-10-02: "no" did nothing, and the question came back). Nothing is left to clean: the engine retired the change at the refused merge (PR closed, pending cleared, lock released), and this read's own retire catches any leftover. 409 when the thread's failed run noted no such conflict. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteModMergeConflict_ - `request_id` · string · required - `success` · true - `data` · WebsiteModMergeConflict · required: A Publish whose merge was refused because the site changed under the draft: what was changed directly, and the change to rebuild on top. - `summary` · string · required - `change` · string - `rebuild_request` · string - `declined` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/website-mod/progress-stream Stream website-mod agent progress events (SSE) Operation id: `stream_website_mod_progress_api_v1_admin_businesses__business_id__website_mod_progress_stream_get` Poll Redis progress for the in-flight plan and emit SSE until terminal. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `after_seq` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: string - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/publish Publish the pending website change (approve + merge-deploy) Operation id: `publish_website_mod_change_api_v1_admin_businesses__business_id__website_mod_publish_post` 202: the approval is recorded and the engine merges to main and deploys. Publish IS the approval — one action, no separate approve step. It is delivered as the control plane's approval decision rather than a direct ``merge_and_deploy`` because the run is parked on that approval signal: a console-side merge would leave the workflow waiting forever on a PR that no longer exists, and the deploy-completion signal it later receives would never be read. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, optional): WebsiteModPublishBody | null - `approval_bundle_ref` · string | null - `comment` · string | null **Responses** - `202` Successful Response: SuccessResponse_WebsiteModPublishResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteModPublishResponse · required: 202 body for publish: the approval was accepted, the deploy follows. - `published` · boolean · required - `thread_id` · string · required - `workflow_run_id` · string · required - `approval_bundle_ref` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/businesses/{business_id}/website-mod/queue/{item_id} Take back a queued request before it goes Operation id: `remove_queued_website_mod_change_api_v1_admin_businesses__business_id__website_mod_queue__item_id__delete` Idempotent: a request no longer queued answers ``removed: false``. 409 ``QUEUE_ITEM_SENDING`` for one already on its way. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_EditorQueueRemoveResponse_ - `request_id` · string · required - `success` · true - `data` · EditorQueueRemoveResponse · required: A queued request taken back: false when it was no longer queued. - `removed` · boolean · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/website-mod/runs The website's recent agent runs, each with its progress timeline Operation id: `get_website_mod_runs_api_v1_admin_businesses__business_id__website_mod_runs_get` What the agent did for each recent request — read back after a page reload so the folded "What the agent did" account stays under the message that asked for it. Two weeks of history, oldest first. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteModRunsResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteModRunsResponse · required - `website_id` · string · required - `runs` · WebsiteModRun[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/website-mod/session Read the website's live-preview session (pilot) Operation id: `get_website_mod_session_api_v1_admin_businesses__business_id__website_mod_session_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteModSessionResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteModSessionResponse · required: The website's live-preview session (pilot): whether the session pod is serving this site, at which commit, and the URL the console frames. - `website_id` · string · required - `enabled` · boolean · required - `status` · string | null - `repo` · string | null - `base_sha` · string | null - `preview_url` · string | null - `timings_s` · object - `slot` · string | null - `expires_at` · number | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/session/heartbeat Keep the website's live-preview session leased while the editor is open Operation id: `heartbeat_website_mod_session_api_v1_admin_businesses__business_id__website_mod_session_heartbeat_post` The console calls this every minute or so while the site editor is open. A slot whose lease nobody extends lapses (the pool's LEASE_TTL) and the pod goes back to the warm floor — so tabs are viewers, and closing the last one is what frees the slot, never a client-side beforeunload. 404 for a website not in session mode; a website holding no lease gets the plain "enabled, nothing serving" response (pre-warm is what claims). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteModSessionResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteModSessionResponse · required: The website's live-preview session (pilot): whether the session pod is serving this site, at which commit, and the URL the console frames. - `website_id` · string · required - `enabled` · boolean · required - `status` · string | null - `repo` · string | null - `base_sha` · string | null - `preview_url` · string | null - `timings_s` · object - `slot` · string | null - `expires_at` · number | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/session/prewarm Pre-warm the website's live-preview session (pilot) Operation id: `prewarm_website_mod_session_api_v1_admin_businesses__business_id__website_mod_session_prewarm_post` Called by the console when the operator opens the site editor, so the pod has cloned, installed and booted `next dev` before the first change request arrives — the whole point of pre-warming. Idempotent: a pod already serving this site at the right commit is left alone. 404 for a website not in session mode, so the console can fire this unconditionally and simply learn that the pilot does not apply here. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsiteModSessionResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteModSessionResponse · required: The website's live-preview session (pilot): whether the session pod is serving this site, at which commit, and the URL the console frames. - `website_id` · string · required - `enabled` · boolean · required - `status` · string | null - `repo` · string | null - `base_sha` · string | null - `preview_url` · string | null - `timings_s` · object - `slot` · string | null - `expires_at` · number | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/stop Stop the change being made, keeping what the site agent has done Operation id: `stop_website_mod_change_api_v1_admin_businesses__business_id__website_mod_stop_post` The owner's Stop: the site agent's turn ends at its next step, and what it changed so far becomes the change to review — publish it, ask for more, or discard it. (Discard is the stop that throws the work away.) 409 when this thread has no change being made, or its run cannot be reached, or the run the Stop names (``plan_request_id``) is not the one being made: a Stop that arrives late must not stop the queued request that followed. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, optional): WebsiteModStopBody | null - `plan_request_id` · string | null **Responses** - `202` Successful Response: SuccessResponse_WebsiteEditorStopResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteEditorStopResponse · required: The owner's Stop on the change being made. - `stopping` · boolean · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-mod/undo Take back the newest version of the change in review Operation id: `undo_website_mod_change_api_v1_admin_businesses__business_id__website_mod_undo_post` The owner's Undo: the change in review goes back to the version before its newest (the branch, the preview, the summary), and the next request builds on that. Only the newest version, while this thread's change waits for review and is not being published; with the version that opened the change left, Discard is the way. What it changed in the business records stays. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): WebsiteModUndoBody - `version_id` · string · required **Responses** - `200` Successful Response: SuccessResponse_WebsiteModUndoResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteModUndoResponse · required: The change in review is back on the version before the one taken back. - `undone` · boolean · required - `newest_version_id` · string · required - `summary` · string - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Console API: Website preview 13 endpoints. HTML: https://developers.keystone.app/api/console/website-preview/ ### GET /api/v1/admin/businesses/{business_id}/website-preview Get the business's website preview, chat, and draft URL Operation id: `get_website_preview_api_v1_admin_businesses__business_id__website_preview_get` Preview + revisions[] (the chat) + the draft URL. Every revision carries its OWN ``preview_url`` (the Worker version uploaded for its checkpoint sha), and the top-level ``preview_url`` is the current revision's — a new build means a NEW URL, so the console no longer has to cache-bust a reused host, and past revisions stay viewable (History). Both are None until the version lands, a minute or two after the revision goes ready. ``live`` is the edit-pool slot serving this preview (live edits), which the console frames instead while it is set; ``session_enabled`` whether this preview's edits run live at all. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsitePreviewGetResponse_ - `request_id` · string · required - `success` · true - `data` · WebsitePreviewGetResponse · required: Preview + the chat + the draft URL (the console's iframe source). ``preview_url`` is the CURRENT revision's version URL — the same value as that revision's own ``preview_url`` — so the console always frames a known build rather than "whatever was uploaded to the worker last". None while that revision's version has not appeared in Cloudflare yet; the console keeps showing the previous revision's URL until it does. ``live`` is the edit-pool slot serving this preview, when one is (the console frames it instead while set); ``session_enabled`` says whether this preview's edits run live at all (the console pre-warms and heartbeats only then). A revision's ``status`` may be ``no_change``: a reply, no new build. - `preview` · WebsitePreviewData · required: Preview row for admin display (no ``brief_text`` — internal). - `revisions` · WebsitePreviewRevisionData[] · required - `preview_url` · string | null - `live` · WebsitePreviewLive | null - `session_enabled` · boolean - `capabilities` · EditorCapabilities - `browser_check` · BrowserCheckView | null - `queue` · EditorQueueState | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-preview Create (or start over) the business's website preview Operation id: `create_website_preview_api_v1_admin_businesses__business_id__website_preview_post` 202: rows are committed and revision 0's generation is spawned; poll GET. Also the "start over" path — an existing (unconfirmed, not in-flight) preview is replaced with a fresh one on the same repo/worker, and the onboarding website stage the replaced draft's failure left ``failed`` runs again with the new one (``reopen_onboarding``: an owner's start-over, which auto-onboarding's own re-entry is not). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, optional): WebsitePreviewCreateBody | null - `user_prompt` · string | null **Responses** - `202` Successful Response: SuccessResponse_WebsitePreviewCreateResponse_ - `request_id` · string · required - `success` · true - `data` · WebsitePreviewCreateResponse · required - `preview` · WebsitePreviewData · required: Preview row for admin display (no ``brief_text`` — internal). - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/website-preview/progress-stream Stream the live preview edit's activity (SSE) Operation id: `stream_website_preview_progress_api_v1_admin_businesses__business_id__website_preview_progress_stream_get` The preview's live run as SSE, in website-mod's frame shape: its events after ``after_seq``, ``reset`` when the run changes, ``done`` when it ends. The console opens it when an edit is sent. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `after_seq` | query | integer | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/admin/businesses/{business_id}/website-preview/runs The preview's past revisions, each with its activity trail Operation id: `get_website_preview_runs_api_v1_admin_businesses__business_id__website_preview_runs_get` What the agent did for each past chat turn, and for the full build once it has run, so the account of the work survives a page reload and outlives the stream (which only ever narrates the one live run). Joined to the chat by ``revision``; the prompts, replies and timings come from the preview read, which the console already has. ``build`` is the launch's own account, filed under the confirm. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsitePreviewRunsResponse_ - `request_id` · string · required - `success` · true - `data` · WebsitePreviewRunsResponse · required: Trails for the preview's recent revisions, oldest last, and the full build's once it has run. Revisions with no stored run are absent, and a run whose ring has aged out (two weeks) comes back with no events — both render as a turn without a trail. - `preview_id` · string (uuid) · required - `runs` · WebsitePreviewRun[] - `build` · WebsitePreviewBuildRun | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-preview/session/heartbeat Keep the preview's live edit session leased while the chat is open Operation id: `heartbeat_website_preview_session_api_v1_admin_businesses__business_id__website_preview_session_heartbeat_post` Every 60 s while the preview chat is open: a lease nobody extends lapses and the slot returns to the warm floor. ``live: null`` when the preview holds no lease (pre-warm is what claims). 404 when edits do not run live. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsitePreviewSessionResponse_ - `request_id` · string · required - `success` · true - `data` · WebsitePreviewSessionResponse · required: Pre-warm / heartbeat answer: the read's two live-edit fields. - `session_enabled` · boolean - `live` · WebsitePreviewLive | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/businesses/{business_id}/website-preview/session/prewarm Pre-warm the preview's live edit session Operation id: `prewarm_website_preview_session_api_v1_admin_businesses__business_id__website_preview_session_prewarm_post` Called once when the preview chat opens, so the slot has cloned, installed and booted the draft before the first edit. Idempotent: a slot already serving the current draft is left alone, and a run in flight keeps what it serves. Nothing to warm (no ready draft yet, or the chat is closed) answers ``live: null``. 404 when the preview's edits do not run live. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsitePreviewSessionResponse_ - `request_id` · string · required - `success` · true - `data` · WebsitePreviewSessionResponse · required: Pre-warm / heartbeat answer: the read's two live-edit fields. - `session_enabled` · boolean - `live` · WebsitePreviewLive | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/website-previews/{preview_id}/check/override Let a draft its browser check holds back be finished (Keystone staff) Operation id: `override_website_preview_check_api_v1_admin_website_previews__preview_id__check_override_post` Keystone staff only: the reason and what the check had found are kept in ``website_check_overrides``. 409 when nothing is held back. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `preview_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): BrowserCheckOverrideBody - `reason` · string · required **Responses** - `200` Successful Response: SuccessResponse_BrowserCheckView_ - `request_id` · string · required - `success` · true - `data` · BrowserCheckView · required: The newest browser check of what Publish (website-mod) or Finish (preview) would ship, and whether it holds that back. - `id` · string · required - `status` · string · required - `blocked` · boolean · required - `problems` · string[] - `pages` · BrowserCheckPage[] - `checked_at` · string (date-time) | null - `overridden` · boolean - `override_reason` · string | null - `can_recheck` · boolean - `rechecking` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/website-previews/{preview_id}/check/recheck Look at the draft's pages in a browser again Operation id: `recheck_website_preview_check_api_v1_admin_website_previews__preview_id__check_recheck_post` 202: the edit slot looks again, without the site agent, at the pages a check that never reached the browser was to look at. The read shows ``rechecking`` until the new check lands. 409 while a revision generates, without live edits, or for a check a look cannot settle. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `preview_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `202` Successful Response: SuccessResponse_BrowserCheckView_ - `request_id` · string · required - `success` · true - `data` · BrowserCheckView · required: The newest browser check of what Publish (website-mod) or Finish (preview) would ship, and whether it holds that back. - `id` · string · required - `status` · string · required - `blocked` · boolean · required - `problems` · string[] - `pages` · BrowserCheckPage[] - `checked_at` · string (date-time) | null - `overridden` · boolean - `override_reason` · string | null - `can_recheck` · boolean - `rechecking` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/website-previews/{preview_id}/confirm Confirm the website preview and start the full build Operation id: `confirm_website_preview_api_v1_admin_website_previews__preview_id__confirm_post` 202: preview → confirmed, draft PR closed (never merged), seeded full build fired after commit. Only a ready preview confirms (409 otherwise). The body is optional and additive: ``{"text": ...}`` persists the customer's confirming chat message on the preview as ``confirm_text`` (it IS the confirmation, in the prototype's conversational shape); no body keeps the original behaviour exactly. With ``website_preview_engine_enabled``, a confirm while a revision is generating answers 202 with ``queued: true`` instead of 409: it runs when that revision settles ``ready``/``no_change``. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `preview_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, optional): WebsitePreviewConfirmBody | null - `text` · string | null **Responses** - `202` Successful Response: SuccessResponse_WebsitePreviewConfirmResponse_ - `request_id` · string · required - `success` · true - `data` · WebsitePreviewConfirmResponse · required: The confirmed preview — including ``confirm_text`` when the caller sent one (additive; a no-body confirm answers exactly as it did before). ``queued`` (engine mode): the confirm arrived while a revision was still generating, so nothing is confirmed yet — it runs when that revision settles ``ready``/``no_change``, and a failed settle clears it. - `preview` · WebsitePreviewData · required: Preview row for admin display (no ``brief_text`` — internal). - `queued` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/website-previews/{preview_id}/edits Send a chat edit to the website preview Operation id: `create_website_preview_edit_api_v1_admin_website_previews__preview_id__edits_post` 202 with revision n+1 spawned; 409 while any revision is generating. The message's INTENT is decided server-side first (see ``website_preview.intent``): a message that reads as approval ("looks good, ship it", "finish my website") runs the SAME confirm flow the dedicated /confirm route runs — the service function is reused, never duplicated — and answers with ``intent: "confirm"`` and no revision. Any other message runs the edit flow exactly as before, with ``intent: "edit"``. The /confirm route itself is untouched (the console chip may still call it directly). A confirm-labelled message the preview cannot confirm (409: not ready, nothing built yet) FALLS THROUGH to the edit flow instead of erroring: the label was the classifier's guess, and dropping the customer's words on the floor because the guess outran the preview's state was a real lost-message bug. The dedicated /confirm route keeps its honest 409 — there the caller SAID confirm. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `preview_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): WebsitePreviewEditBody - `text` · string | null · required - `model_id` · string | null - `attachments` · WebsiteAgentAttachmentRef[] - `kind` · "photo" | "document" · required - `id` · string · required - `client_message_id` · string | null - `delivery` · "now" | "queue" | null **Responses** - `202` Successful Response: SuccessResponse_WebsitePreviewEditResponse_ - `request_id` · string · required - `success` · true - `data` · WebsitePreviewEditResponse · required: 202 body for a chat message on the preview. ``intent`` says which flow the server ran for the message: ``edit`` (a new revision was spawned — ``revision`` carries it, exactly as before) or ``confirm`` (the message read as approval, so the CONFIRM flow ran — the preview is confirmed, the full build is armed, and ``revision`` is None because confirm never creates a revision row). Additive: pre-intent consumers of the edit path see the same shape they always did. - `preview` · WebsitePreviewData · required: Preview row for admin display (no ``brief_text`` — internal). - `revision` · WebsitePreviewRevisionData | null - `intent` · string - `queued` · boolean - `steered` · boolean - `queued_request_id` · string | null - `note_refused` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/admin/website-previews/{preview_id}/queue/{item_id} Take back a queued draft edit before it goes Operation id: `remove_queued_website_preview_edit_api_v1_admin_website_previews__preview_id__queue__item_id__delete` Idempotent: a request no longer queued answers ``removed: false``. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `preview_id` | path | string (uuid) | yes | | | `item_id` | path | string | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_EditorQueueRemoveResponse_ - `request_id` · string · required - `success` · true - `data` · EditorQueueRemoveResponse · required: A queued request taken back: false when it was no longer queued. - `removed` · boolean · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/website-previews/{preview_id}/stop Stop the draft edit being made, keeping what the site agent has done Operation id: `stop_website_preview_edit_api_v1_admin_website_previews__preview_id__stop_post` The owner's Stop: the site agent's turn ends at its next step, and what it changed so far settles as the revision (nothing changed: no change). 409 when no edit is being made on a session pod (the job cannot be stopped) or the pod cannot be reached; an edit still on its way to its pod is stopped once it gets there. ``revision`` names the edit the Stop is for: a late Stop, once that edit settled, never stops the queued one. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `preview_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, optional): WebsitePreviewStopBody | null - `revision` · integer | null **Responses** - `202` Successful Response: SuccessResponse_WebsiteEditorStopResponse_ - `request_id` · string · required - `success` · true - `data` · WebsiteEditorStopResponse · required: The owner's Stop on the change being made. - `stopping` · boolean · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/admin/website-previews/{preview_id}/undo Take back the draft's newest edit Operation id: `undo_website_preview_edit_api_v1_admin_website_previews__preview_id__undo_post` The owner's Undo: the draft goes back to the edit before the current one (the first draft at the earliest), and the next edit builds on it. 409 while an edit is being made or a finish is queued, and with nothing to undo. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `preview_id` | path | string (uuid) | yes | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsitePreviewUndoResponse_ - `request_id` · string · required - `success` · true - `data` · WebsitePreviewUndoResponse · required: The draft is back on the edit before the one taken back. - `preview` · WebsitePreviewData · required: Preview row for admin display (no ``brief_text`` — internal). - `undone` · WebsitePreviewRevisionData · required: One revision — a chat turn: the prompt that caused it plus its outcome. ``trigger`` ``initial``/``chat`` rows are generation runs; a ``confirm`` row is the customer's closing message and carries no run (status ``confirmed``, no ``checkpoint_sha``, no ``run_id``). - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse # Agent API Per-business CRUD the platform's agents use to write business information. Service-to-service endpoints the Keystone agents (website, content, social, listings and the rest) call to read and write a business's records. Each resource mirrors a Business Info section. - Audience: Keystone services - Base URL: https://sor.keystone.app - Authentication: Internal service key, sent as the `X-Internal-Api-Key` header. - Endpoints: 46 in 8 groups - HTML: https://developers.keystone.app/api/agent/ ## Agent API: Business 2 endpoints. HTML: https://developers.keystone.app/api/agent/business/ ### GET /api/v1/agent/businesses/{business_id} Get business details (agent) Operation id: `get_business_api_v1_agent_businesses__business_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BusinessProfileData_ - `request_id` · string · required - `success` · true - `data` · BusinessProfileData · required: Business profile for GET response. - `id` · string · required - `company_name` · string | null - `year_founded` · integer | null - `website_url` · string | null - `tagline` · string | null - `address` · Address | null - `descriptions` · Descriptions | null - `contact_info` · ContactInfo | null - `social_profiles` · SocialProfiles | null - `industries` · BusinessIndustries | null - `terms_of_service_markdown` · string | null - `privacy_policy_markdown` · string | null - `is_active` · boolean - `timezone` · string - `timezone_source` · string - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/agent/businesses/{business_id} Update business profile (agent) Operation id: `patch_business_api_v1_agent_businesses__business_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): BusinessUpdateBody - `company_name` · string | null - `year_founded` · integer | null - `website_url` · string | null - `tagline` · string | null - `address` · Address | null - `line1` · string | null - `line2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `country` · string | null - `descriptions` · Descriptions | null - `company_description` · string | null - `about` · string | null - `mission_statement` · string | null - `values` · string[] - `contact_info` · ContactInfo | null - `primary_phone` · string | null - `primary_email` · string | null - `support_email` · string | null - `sales_email` · string | null - `external_management_url` · string | null - `social_profiles` · SocialProfiles | null - `facebook` · string | null - `instagram` · string | null - `twitter` · string | null - `linkedin` · string | null - `youtube` · string | null - `tiktok` · string | null - `google_my_business` · string | null - `pinterest` · string | null - `yelp` · string | null - `tripadvisor` · string | null - `google_review` · string | null - `industries` · BusinessIndustries | null - `primary` · string | null - `secondary` · string[] - `terms_of_service_markdown` · string | null - `privacy_policy_markdown` · string | null - `is_active` · boolean | null - `timezone` · string | null **Responses** - `200` Successful Response: SuccessResponse_BusinessProfileData_ - `request_id` · string · required - `success` · true - `data` · BusinessProfileData · required: Business profile for GET response. - `id` · string · required - `company_name` · string | null - `year_founded` · integer | null - `website_url` · string | null - `tagline` · string | null - `address` · Address | null - `descriptions` · Descriptions | null - `contact_info` · ContactInfo | null - `social_profiles` · SocialProfiles | null - `industries` · BusinessIndustries | null - `terms_of_service_markdown` · string | null - `privacy_policy_markdown` · string | null - `is_active` · boolean - `timezone` · string - `timezone_source` · string - `created_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Agent API: FAQ 6 endpoints. HTML: https://developers.keystone.app/api/agent/faq/ ### GET /api/v1/agent/businesses/{business_id}/faqs List FAQs (agent) Operation id: `list_faqs_api_v1_agent_businesses__business_id__faqs_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_FaqData__ - `request_id` · string · required - `success` · true - `data` · FaqData[] · required - `id` · string · required - `business_id` · string · required - `question` · string · required - `answer` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/agent/businesses/{business_id}/faqs Create FAQ (agent) Operation id: `create_faq_api_v1_agent_businesses__business_id__faqs_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): FaqCreateBody - `question` · string · required - `answer` · string · required - `photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_FaqData_ - `request_id` · string · required - `success` · true - `data` · FaqData · required: FAQ for list/GET response. - `id` · string · required - `business_id` · string · required - `question` · string · required - `answer` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/agent/businesses/{business_id}/faqs/{faq_id} Get FAQ (agent) Operation id: `get_faq_api_v1_agent_businesses__business_id__faqs__faq_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `faq_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_FaqData_ - `request_id` · string · required - `success` · true - `data` · FaqData · required: FAQ for list/GET response. - `id` · string · required - `business_id` · string · required - `question` · string · required - `answer` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/agent/businesses/{business_id}/faqs/{faq_id} Update FAQ (agent) Operation id: `update_faq_api_v1_agent_businesses__business_id__faqs__faq_id__put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `faq_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): FaqUpdateBody - `question` · string | null - `answer` · string | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_FaqData_ - `request_id` · string · required - `success` · true - `data` · FaqData · required: FAQ for list/GET response. - `id` · string · required - `business_id` · string · required - `question` · string · required - `answer` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/agent/businesses/{business_id}/faqs/{faq_id} Partial update FAQ (agent) Operation id: `patch_faq_api_v1_agent_businesses__business_id__faqs__faq_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `faq_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): FaqUpdateBody - `question` · string | null - `answer` · string | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_FaqData_ - `request_id` · string · required - `success` · true - `data` · FaqData · required: FAQ for list/GET response. - `id` · string · required - `business_id` · string · required - `question` · string · required - `answer` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/agent/businesses/{business_id}/faqs/{faq_id} Delete FAQ (agent) Operation id: `delete_faq_api_v1_agent_businesses__business_id__faqs__faq_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `faq_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Agent API: Jobs 6 endpoints. HTML: https://developers.keystone.app/api/agent/jobs/ ### GET /api/v1/agent/businesses/{business_id}/job_postings List job postings (agent) Operation id: `list_job_postings_api_v1_agent_businesses__business_id__job_postings_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_JobPostingData__ - `request_id` · string · required - `success` · true - `data` · JobPostingData[] · required - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus · required: Job posting status enum. - `featured` · boolean - `display_order` · integer - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] - `photos` · PhotoData[] - `applications_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/agent/businesses/{business_id}/job_postings Create job posting (agent) Operation id: `create_job_posting_api_v1_agent_businesses__business_id__job_postings_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): JobPostingCreateBody - `title` · string · required - `slug` · string | null - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus: Job posting status enum. - `featured` · boolean - `display_order` · integer | null - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_JobPostingData_ - `request_id` · string · required - `success` · true - `data` · JobPostingData · required: Job posting for admin list/GET response. - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus · required: Job posting status enum. - `featured` · boolean - `display_order` · integer - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] - `photos` · PhotoData[] - `applications_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/agent/businesses/{business_id}/job_postings/{job_posting_id} Get job posting (agent) Operation id: `get_job_posting_api_v1_agent_businesses__business_id__job_postings__job_posting_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `job_posting_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_JobPostingData_ - `request_id` · string · required - `success` · true - `data` · JobPostingData · required: Job posting for admin list/GET response. - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus · required: Job posting status enum. - `featured` · boolean - `display_order` · integer - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] - `photos` · PhotoData[] - `applications_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/agent/businesses/{business_id}/job_postings/{job_posting_id} Update job posting (agent) Operation id: `update_job_posting_api_v1_agent_businesses__business_id__job_postings__job_posting_id__put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `job_posting_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): JobPostingUpdateBody - `title` · string | null - `slug` · string | null - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType | null - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus | null - `featured` · boolean | null - `display_order` · integer | null - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_JobPostingData_ - `request_id` · string · required - `success` · true - `data` · JobPostingData · required: Job posting for admin list/GET response. - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus · required: Job posting status enum. - `featured` · boolean - `display_order` · integer - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] - `photos` · PhotoData[] - `applications_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/agent/businesses/{business_id}/job_postings/{job_posting_id} Partial update job posting (agent) Operation id: `patch_job_posting_api_v1_agent_businesses__business_id__job_postings__job_posting_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `job_posting_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): JobPostingUpdateBody - `title` · string | null - `slug` · string | null - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType | null - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus | null - `featured` · boolean | null - `display_order` · integer | null - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_JobPostingData_ - `request_id` · string · required - `success` · true - `data` · JobPostingData · required: Job posting for admin list/GET response. - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `description_markdown` · string | null - `requirements_markdown` · string | null - `responsibilities_markdown` · string | null - `benefits_markdown` · string | null - `employment_type` · JobEmploymentType · required: Employment type enum for job postings. - `experience_level` · string | null - `location` · string | null - `salary_range` · string | null - `status` · JobStatus · required: Job posting status enum. - `featured` · boolean - `display_order` · integer - `posted_at` · string (date-time) | null - `expires_at` · string (date-time) | null - `photo_ids` · string[] - `photos` · PhotoData[] - `applications_count` · integer - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/agent/businesses/{business_id}/job_postings/{job_posting_id} Delete job posting (agent) Operation id: `delete_job_posting_api_v1_agent_businesses__business_id__job_postings__job_posting_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `job_posting_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Agent API: Locations 6 endpoints. HTML: https://developers.keystone.app/api/agent/locations/ ### GET /api/v1/agent/businesses/{business_id}/locations List locations (agent) Operation id: `list_locations_api_v1_agent_businesses__business_id__locations_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_LocationData__ - `request_id` · string · required - `success` · true - `data` · LocationData[] · required - `id` · string · required - `business_id` · string · required - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `slug` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/agent/businesses/{business_id}/locations Create location (agent) Operation id: `create_location_api_v1_agent_businesses__business_id__locations_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): LocationCreateBody - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `line1` · string | null - `line2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `country` · string | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `monday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `tuesday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `wednesday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `thursday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `friday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `saturday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `sunday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `slug` · string | null - `photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_LocationData_ - `request_id` · string · required - `success` · true - `data` · LocationData · required: Location for list/GET response. - `id` · string · required - `business_id` · string · required - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `slug` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/agent/businesses/{business_id}/locations/{location_id} Get location (agent) Operation id: `get_location_api_v1_agent_businesses__business_id__locations__location_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `location_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_LocationData_ - `request_id` · string · required - `success` · true - `data` · LocationData · required: Location for list/GET response. - `id` · string · required - `business_id` · string · required - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `slug` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/agent/businesses/{business_id}/locations/{location_id} Update location (agent) Operation id: `update_location_api_v1_agent_businesses__business_id__locations__location_id__put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `location_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): LocationUpdateBody - `location_name` · string | null - `is_primary_location` · boolean | null - `is_active_location` · boolean | null - `address` · Address | null - `line1` · string | null - `line2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `country` · string | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `monday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `tuesday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `wednesday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `thursday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `friday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `saturday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `sunday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `slug` · string | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_LocationData_ - `request_id` · string · required - `success` · true - `data` · LocationData · required: Location for list/GET response. - `id` · string · required - `business_id` · string · required - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `slug` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/agent/businesses/{business_id}/locations/{location_id} Partial update location (agent) Operation id: `patch_location_api_v1_agent_businesses__business_id__locations__location_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `location_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): LocationUpdateBody - `location_name` · string | null - `is_primary_location` · boolean | null - `is_active_location` · boolean | null - `address` · Address | null - `line1` · string | null - `line2` · string | null - `city` · string | null - `state` · string | null - `zip_code` · string | null - `country` · string | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `monday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `tuesday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `wednesday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `thursday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `friday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `saturday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `sunday` · DayHours | null - `is_open` · boolean - `open_time` · string | null - `close_time` · string | null - `slug` · string | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_LocationData_ - `request_id` · string · required - `success` · true - `data` · LocationData · required: Location for list/GET response. - `id` · string · required - `business_id` · string · required - `location_name` · string · required - `is_primary_location` · boolean - `is_active_location` · boolean - `address` · Address | null - `phone` · string | null - `email` · string | null - `description` · string | null - `timezone` · string | null - `business_hours` · BusinessHours | null - `slug` · string · required - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/agent/businesses/{business_id}/locations/{location_id} Delete location (agent) Operation id: `delete_location_api_v1_agent_businesses__business_id__locations__location_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `location_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Agent API: Packages 7 endpoints. HTML: https://developers.keystone.app/api/agent/packages/ ### GET /api/v1/agent/businesses/{business_id}/packages List packages (agent) Operation id: `list_packages_api_v1_agent_businesses__business_id__packages_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_PackageData__ - `request_id` · string · required - `success` · true - `data` · PackageData[] · required - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/agent/businesses/{business_id}/packages Create package (agent) Operation id: `create_package_api_v1_agent_businesses__business_id__packages_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PackageCreateBody - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · number | string | null - `slug` · string | null - `photo_ids` · string[] - `is_featured` · boolean | null **Responses** - `201` Successful Response: SuccessResponse_PackageData_ - `request_id` · string · required - `success` · true - `data` · PackageData · required: Package for list/GET response. `service_items` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-package member queries. - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/agent/businesses/{business_id}/packages/{package_id} Get package (agent) Operation id: `get_package_api_v1_agent_businesses__business_id__packages__package_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PackageData_ - `request_id` · string · required - `success` · true - `data` · PackageData · required: Package for list/GET response. `service_items` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-package member queries. - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/agent/businesses/{business_id}/packages/{package_id} Update package (agent) Operation id: `update_package_api_v1_agent_businesses__business_id__packages__package_id__put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PackageUpdateBody - `package_name` · string | null - `package_description` · string | null - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · number | string | null - `slug` · string | null - `photo_ids` · string[] | null - `is_featured` · boolean | null **Responses** - `200` Successful Response: SuccessResponse_PackageData_ - `request_id` · string · required - `success` · true - `data` · PackageData · required: Package for list/GET response. `service_items` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-package member queries. - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/agent/businesses/{business_id}/packages/{package_id} Partial update package (agent) Operation id: `patch_package_api_v1_agent_businesses__business_id__packages__package_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PackageUpdateBody - `package_name` · string | null - `package_description` · string | null - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · number | string | null - `slug` · string | null - `photo_ids` · string[] | null - `is_featured` · boolean | null **Responses** - `200` Successful Response: SuccessResponse_PackageData_ - `request_id` · string · required - `success` · true - `data` · PackageData · required: Package for list/GET response. `service_items` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-package member queries. - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/agent/businesses/{business_id}/packages/{package_id} Delete package (agent) Operation id: `delete_package_api_v1_agent_businesses__business_id__packages__package_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/agent/businesses/{business_id}/packages/{package_id}/service-items Set package member service items (agent) Operation id: `set_package_service_items_api_v1_agent_businesses__business_id__packages__package_id__service_items_put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `package_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PackageServiceItemsBody - `ordered_service_item_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_PackageData_ - `request_id` · string · required - `success` · true - `data` · PackageData · required: Package for list/GET response. `service_items` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-package member queries. - `id` · string · required - `business_id` · string · required - `package_name` · string · required - `package_description` · string · required - `package_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Agent API: Service items 7 endpoints. HTML: https://developers.keystone.app/api/agent/service-items/ ### GET /api/v1/agent/businesses/{business_id}/service-items List service items (agent) Operation id: `list_service_items_api_v1_agent_businesses__business_id__service_items_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ServiceItemData__ - `request_id` · string · required - `success` · true - `data` · ServiceItemData[] · required - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/agent/businesses/{business_id}/service-items Create service item (agent) Operation id: `create_service_item_api_v1_agent_businesses__business_id__service_items_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ServiceItemCreateBody - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · number | string | null - `slug` · string | null - `photo_ids` · string[] - `is_featured` · boolean | null **Responses** - `201` Successful Response: SuccessResponse_ServiceItemData_ - `request_id` · string · required - `success` · true - `data` · ServiceItemData · required: Service item for list/GET response. `services` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-item member queries. - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/agent/businesses/{business_id}/service-items/{item_id} Get service item (agent) Operation id: `get_service_item_api_v1_agent_businesses__business_id__service_items__item_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ServiceItemData_ - `request_id` · string · required - `success` · true - `data` · ServiceItemData · required: Service item for list/GET response. `services` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-item member queries. - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/agent/businesses/{business_id}/service-items/{item_id} Update service item (agent) Operation id: `update_service_item_api_v1_agent_businesses__business_id__service_items__item_id__put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ServiceItemUpdateBody - `item_name` · string | null - `item_description` · string | null - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · number | string | null - `slug` · string | null - `photo_ids` · string[] | null - `is_featured` · boolean | null **Responses** - `200` Successful Response: SuccessResponse_ServiceItemData_ - `request_id` · string · required - `success` · true - `data` · ServiceItemData · required: Service item for list/GET response. `services` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-item member queries. - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/agent/businesses/{business_id}/service-items/{item_id} Partial update service item (agent) Operation id: `patch_service_item_api_v1_agent_businesses__business_id__service_items__item_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ServiceItemUpdateBody - `item_name` · string | null - `item_description` · string | null - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · number | string | null - `slug` · string | null - `photo_ids` · string[] | null - `is_featured` · boolean | null **Responses** - `200` Successful Response: SuccessResponse_ServiceItemData_ - `request_id` · string · required - `success` · true - `data` · ServiceItemData · required: Service item for list/GET response. `services` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-item member queries. - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/agent/businesses/{business_id}/service-items/{item_id} Delete service item (agent) Operation id: `delete_service_item_api_v1_agent_businesses__business_id__service_items__item_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/agent/businesses/{business_id}/service-items/{item_id}/services Set service item member services (agent) Operation id: `set_item_services_api_v1_agent_businesses__business_id__service_items__item_id__services_put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `item_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ServiceItemServicesBody - `ordered_service_ids` · string[] · required **Responses** - `200` Successful Response: SuccessResponse_ServiceItemData_ - `request_id` · string · required - `success` · true - `data` · ServiceItemData · required: Service item for list/GET response. `services` is only populated on detail responses (GET by id); list endpoints return it empty to avoid per-item member queries. - `id` · string · required - `business_id` · string · required - `item_name` · string · required - `item_description` · string · required - `item_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `cost` · string | null - `slug` · string · required - `photos` · PhotoData[] - `services` · ServiceData[] - `offers` · OfferData[] - `display_order` · integer - `is_featured` · boolean | null - `created_at` · integer - `updated_at` · integer - `deleted_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Agent API: Services 6 endpoints. HTML: https://developers.keystone.app/api/agent/services/ ### GET /api/v1/agent/businesses/{business_id}/services List services (agent) Operation id: `list_services_api_v1_agent_businesses__business_id__services_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ServiceData__ - `request_id` · string · required - `success` · true - `data` · ServiceData[] · required - `id` · string · required - `business_id` · string · required - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `service_type` · string - `parent_service_id` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/agent/businesses/{business_id}/services Create service (agent) Operation id: `create_service_api_v1_agent_businesses__business_id__services_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ServiceCreateBody - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `slug` · string | null - `photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_ServiceData_ - `request_id` · string · required - `success` · true - `data` · ServiceData · required: Service for list/GET response. - `id` · string · required - `business_id` · string · required - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `service_type` · string - `parent_service_id` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/agent/businesses/{business_id}/services/{service_id} Get service (agent) Operation id: `get_service_api_v1_agent_businesses__business_id__services__service_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `service_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ServiceData_ - `request_id` · string · required - `success` · true - `data` · ServiceData · required: Service for list/GET response. - `id` · string · required - `business_id` · string · required - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `service_type` · string - `parent_service_id` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/agent/businesses/{business_id}/services/{service_id} Update service (agent) Operation id: `update_service_api_v1_agent_businesses__business_id__services__service_id__put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `service_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ServiceUpdateBody - `service_name` · string | null - `service_description` · string | null - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `slug` · string | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_ServiceData_ - `request_id` · string · required - `success` · true - `data` · ServiceData · required: Service for list/GET response. - `id` · string · required - `business_id` · string · required - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `service_type` · string - `parent_service_id` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/agent/businesses/{business_id}/services/{service_id} Partial update service (agent) Operation id: `patch_service_api_v1_agent_businesses__business_id__services__service_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `service_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ServiceUpdateBody - `service_name` · string | null - `service_description` · string | null - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `slug` · string | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_ServiceData_ - `request_id` · string · required - `success` · true - `data` · ServiceData · required: Service for list/GET response. - `id` · string · required - `business_id` · string · required - `service_name` · string · required - `service_description` · string · required - `service_summary` · string | null - `pricing_info` · string | null - `features` · string | null - `service_type` · string - `parent_service_id` · string | null - `slug` · string · required - `photos` · PhotoData[] - `service_items` · ServiceItemSummaryData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/agent/businesses/{business_id}/services/{service_id} Delete service (agent) Operation id: `delete_service_api_v1_agent_businesses__business_id__services__service_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `service_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Agent API: Team 6 endpoints. HTML: https://developers.keystone.app/api/agent/team/ ### GET /api/v1/agent/businesses/{business_id}/team_members List team members (agent) Operation id: `list_team_members_api_v1_agent_businesses__business_id__team_members_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_TeamMemberData__ - `request_id` · string · required - `success` · true - `data` · TeamMemberData[] · required - `id` · string · required - `business_id` · string · required - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/agent/businesses/{business_id}/team_members Create team member (agent) Operation id: `create_team_member_api_v1_agent_businesses__business_id__team_members_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): TeamMemberCreateBody - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photo_ids` · string[] **Responses** - `201` Successful Response: SuccessResponse_TeamMemberData_ - `request_id` · string · required - `success` · true - `data` · TeamMemberData · required: Team member for list/GET response. - `id` · string · required - `business_id` · string · required - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/agent/businesses/{business_id}/team_members/{team_member_id} Get team member (agent) Operation id: `get_team_member_api_v1_agent_businesses__business_id__team_members__team_member_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `team_member_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_TeamMemberData_ - `request_id` · string · required - `success` · true - `data` · TeamMemberData · required: Team member for list/GET response. - `id` · string · required - `business_id` · string · required - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/agent/businesses/{business_id}/team_members/{team_member_id} Update team member (agent) Operation id: `update_team_member_api_v1_agent_businesses__business_id__team_members__team_member_id__put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `team_member_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): TeamMemberUpdateBody - `full_name` · string | null - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_TeamMemberData_ - `request_id` · string · required - `success` · true - `data` · TeamMemberData · required: Team member for list/GET response. - `id` · string · required - `business_id` · string · required - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/agent/businesses/{business_id}/team_members/{team_member_id} Partial update team member (agent) Operation id: `patch_team_member_api_v1_agent_businesses__business_id__team_members__team_member_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `team_member_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): TeamMemberUpdateBody - `full_name` · string | null - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photo_ids` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_TeamMemberData_ - `request_id` · string · required - `success` · true - `data` · TeamMemberData · required: Team member for list/GET response. - `id` · string · required - `business_id` · string · required - `full_name` · string · required - `position_title` · string | null - `post_nominal_letters` · string | null - `phone` · string | null - `email` · string | null - `bio` · string | null - `experience` · string | null - `specialties` · string | null - `education` · string | null - `certifications` · string | null - `linkedin_url` · string | null - `twitter_url` · string | null - `instagram_url` · string | null - `facebook_url` · string | null - `youtube_url` · string | null - `tiktok_url` · string | null - `is_featured` · boolean | null - `photos` · PhotoData[] - `display_order` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/agent/businesses/{business_id}/team_members/{team_member_id} Delete team member (agent) Operation id: `delete_team_member_api_v1_agent_businesses__business_id__team_members__team_member_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `team_member_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse # Internal API Service-to-service operations: onboarding, workflows, media, previews, callbacks. Endpoints other Keystone services call: onboarding and import pipelines, the workflow engine, media processing, website preview runs, agent callbacks, and consumer auth. Not for external callers; listed so that the whole contract is in one place. - Audience: Keystone services - Base URL: https://sor.keystone.app - Authentication: Internal service key, sent as the `X-Internal-Api-Key` header. - Endpoints: 157 in 25 groups - HTML: https://developers.keystone.app/api/internal/ ## Internal API: Account invites 1 endpoints. HTML: https://developers.keystone.app/api/internal/account-invites/ ### POST /api/v1/internal/account-invites/delivery-status Record Account Invite Delivery Status Operation id: `record_account_invite_delivery_status_api_v1_internal_account_invites_delivery_status_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): AccountInviteDeliveryStatusBody - `invite_id` · string (uuid) | null - `email` · string (email) | null - `status` · string · required - `detail` · string | null - `event_at` · integer | null **Responses** - `200` Successful Response: SuccessResponse_AccountInviteDeliveryStatusData_ - `request_id` · string · required - `success` · true - `data` · AccountInviteDeliveryStatusData · required - `updated` · boolean · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Account user invites 1 endpoints. HTML: https://developers.keystone.app/api/internal/account-user-invites/ ### POST /api/v1/internal/account-user-invites/{invite_id}/consume Consume Account Invite Operation id: `consume_account_invite_api_v1_internal_account_user_invites__invite_id__consume_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `invite_id` | path | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): AccountInviteConsumeBody - `user_id` · string (uuid) · required - `email` · string (email) · required - `first_name` · string · required - `last_name` · string · required **Responses** - `200` Successful Response: SuccessResponse_AccountInviteConsumeData_ - `request_id` · string · required - `success` · true - `data` · AccountInviteConsumeData · required - `business_id` · string · required - `user` · AccountUserData · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Ads workflow engine 18 endpoints. HTML: https://developers.keystone.app/api/internal/ads-workflow-engine/ ### POST /api/v1/internal/workflow-engine/ads/archive-campaign Archive Campaign Operation id: `archive_campaign_api_v1_internal_workflow_engine_ads_archive_campaign_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolArchiveCampaignBody - `businessId` · string (uuid) · required - `campaignId` · string (uuid) · required - `userConfirmed` · boolean **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/create-ad-unit Create Ad Unit Operation id: `create_ad_unit_api_v1_internal_workflow_engine_ads_create_ad_unit_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolCreateAdUnitBody - `businessId` · string (uuid) · required - `adSetId` · string (uuid) · required - `photoId` · string (uuid) · required **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/create-refresh-draft Create Refresh Draft Operation id: `create_refresh_draft_api_v1_internal_workflow_engine_ads_create_refresh_draft_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolCreateRefreshDraftBody - `businessId` · string (uuid) · required - `recommendationId` · string (uuid) · required - `action` · "refresh_creative" | "refresh_audience" · required - `photoIds` · string (uuid)[] · required - `adCopy` · string | null - `headline` · string | null - `targetingAdjustments` · object | null - `rationale` · string | null - `mediaBrief` · object | null - `mediaCandidates` · object[] | null - `mediaReview` · object | null - `workflowRunId` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/disable-all-units Disable All Units Operation id: `disable_all_units_api_v1_internal_workflow_engine_ads_disable_all_units_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolDisableAllUnitsBody - `businessId` · string (uuid) · required - `adSetId` · string (uuid) · required - `userConfirmed` · boolean **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/draft-proposal Draft Campaign Proposal Operation id: `draft_campaign_proposal_api_v1_internal_workflow_engine_ads_draft_proposal_post` Non-mutating: enrich the workflow-engine's router-extracted hints into a complete proposal (name, ad copy, headline, address, lat/lng, page, currency) so the ``ADS_CAMPAIGN_PROPOSAL`` widget renders something the user can actually approve. The temporal worker calls this just before parking at ``bp_ads_creator_proposal`` and stuffs the response into ``extracted_data.plan``. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolDraftCampaignProposalBody - `businessId` · string (uuid) · required - `promotion` · string | null - `dailyBudgetMinor` · integer | null - `radiusMeters` · integer | null - `objective` · string | null - `adsAccountId` · string (uuid) | null - `addressLine` · string | null - `attachedPhotoIds` · string[] | null - `adSets` · object[] | null - `businessContext` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/generate-refresh-plan Generate Refresh Plan Operation id: `generate_refresh_plan_api_v1_internal_workflow_engine_ads_generate_refresh_plan_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolGenerateRefreshPlanBody - `businessId` · string (uuid) · required - `entityType` · "campaign" | "ad_set" | "ad_unit" · required - `entityId` · string (uuid) · required - `recommendationId` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/get-campaign Get Campaign Operation id: `get_campaign_api_v1_internal_workflow_engine_ads_get_campaign_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolGetCampaignBody - `businessId` · string (uuid) · required - `campaignId` · string (uuid) · required **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/get-insight-tile Get Insight Tile Operation id: `get_insight_tile_api_v1_internal_workflow_engine_ads_get_insight_tile_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolGetInsightTileBody - `businessId` · string (uuid) · required - `entityType` · "campaign" | "ad_set" | "ad_unit" · required - `entityId` · string (uuid) · required **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/get-refresh-context Get Refresh Context Operation id: `get_refresh_context_api_v1_internal_workflow_engine_ads_get_refresh_context_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolGetRefreshContextBody - `businessId` · string (uuid) · required - `entityType` · "campaign" | "ad_set" | "ad_unit" · required - `entityId` · string (uuid) · required - `recommendationId` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/list-ad-sets List Ad Sets Operation id: `list_ad_sets_api_v1_internal_workflow_engine_ads_list_ad_sets_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolListAdSetsBody - `businessId` · string (uuid) · required - `campaignId` · string (uuid) · required **Responses** - `200` Successful Response: SuccessResponse_list_dict__ - `request_id` · string · required - `success` · true - `data` · object[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/list-campaigns List Campaigns Operation id: `list_campaigns_api_v1_internal_workflow_engine_ads_list_campaigns_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolListCampaignsBody - `businessId` · string (uuid) · required - `status` · string | null - `q` · string | null - `limit` · integer **Responses** - `200` Successful Response: SuccessResponse_list_dict__ - `request_id` · string · required - `success` · true - `data` · object[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/propose-campaign Propose Campaign Operation id: `propose_campaign_api_v1_internal_workflow_engine_ads_propose_campaign_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolProposeCampaignBody - `businessId` · string (uuid) · required - `plan` · object · required - `actorUserId` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/review-refresh-media Review Refresh Media Operation id: `review_refresh_media_api_v1_internal_workflow_engine_ads_review_refresh_media_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolReviewRefreshMediaBody - `businessId` · string (uuid) · required - `mediaBrief` · object | null - `candidates` · object[] · required **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/toggle-unit Toggle Unit Operation id: `toggle_unit_api_v1_internal_workflow_engine_ads_toggle_unit_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolToggleUnitBody - `businessId` · string (uuid) · required - `unitId` · string (uuid) · required - `enabled` · boolean · required **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/update-ad-set Update Ad Set Operation id: `update_ad_set_api_v1_internal_workflow_engine_ads_update_ad_set_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolUpdateAdSetBody - `businessId` · string (uuid) · required - `adSetId` · string (uuid) · required - `patch` · object · required **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/update-ad-unit Update Ad Unit Operation id: `update_ad_unit_api_v1_internal_workflow_engine_ads_update_ad_unit_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolUpdateAdUnitBody - `businessId` · string (uuid) · required - `unitId` · string (uuid) · required - `patch` · object · required **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/update-campaign Update Campaign Operation id: `update_campaign_api_v1_internal_workflow_engine_ads_update_campaign_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolUpdateCampaignBody - `businessId` · string (uuid) · required - `campaignId` · string (uuid) · required - `patch` · object · required **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/ads/update-refresh-status Update Refresh Status Operation id: `update_refresh_status_api_v1_internal_workflow_engine_ads_update_refresh_status_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolUpdateRefreshStatusBody - `businessId` · string (uuid) · required - `recommendationId` · string (uuid) · required - `status` · "failed" | "generating" | "superseded" · required - `workflowRunId` · string | null - `failureCode` · string | null - `errorMessage` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Blog posts 6 endpoints. HTML: https://developers.keystone.app/api/internal/blog-internal/ ### GET /api/v1/internal/businesses/{business_id}/blog_posts List blog posts for a business (internal) Operation id: `list_blog_posts_api_v1_internal_businesses__business_id__blog_posts_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string | no | Status bucket filter: all\|draft\|published\|pending\|archived | | `search` | query | string \| null | no | Search title and excerpt | | `cursor` | query | string \| null | no | Pagination cursor | | `limit` | query | integer | no | Page size | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_BlogPostListItemData__ - `request_id` · string · required - `success` · true - `data` · BlogPostListItemData[] · required - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `status` · string · required - `status_bucket` · string · required - `publish_date` · string | null - `thumbnail_photo_id` · string | null - `author_preview` · BlogPostAuthorPreviewData[] - `excerpt` · string | null - `is_featured` · boolean - `media_status` · string | null - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/blog_posts Create a blog post (internal) Operation id: `create_blog_post_api_v1_internal_businesses__business_id__blog_posts_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): BlogPostWriteBody - `title` · string · required - `slug` · string · required - `status` · string · required - `publish_date` · string | null - `excerpt` · string | null - `content_markdown` · string | null - `content_html` · string | null - `tags` · string[] - `photo_ids` · string[] - `is_featured` · boolean - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] - `author_team_member_ids` · string[] - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null - `brief` · object | null **Responses** - `201` Successful Response: SuccessResponse_BlogPostData_ - `request_id` · string · required - `success` · true - `data` · BlogPostData · required - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `status` · string · required - `status_bucket` · string · required - `publish_date` · string | null - `excerpt` · string | null - `content_markdown` · string | null - `content_html` · string | null - `tags` · string[] | null - `photo_ids` · string[] | null - `is_featured` · boolean - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] | null - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null - `brief` · object | null - `author_preview` · BlogPostAuthorPreviewData[] - `authors` · BlogPostAuthorData[] - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/blog_posts/{blog_post_id} Get a single blog post (internal) Operation id: `get_blog_post_api_v1_internal_businesses__business_id__blog_posts__blog_post_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `blog_post_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_BlogPostData_ - `request_id` · string · required - `success` · true - `data` · BlogPostData · required - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `status` · string · required - `status_bucket` · string · required - `publish_date` · string | null - `excerpt` · string | null - `content_markdown` · string | null - `content_html` · string | null - `tags` · string[] | null - `photo_ids` · string[] | null - `is_featured` · boolean - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] | null - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null - `brief` · object | null - `author_preview` · BlogPostAuthorPreviewData[] - `authors` · BlogPostAuthorData[] - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/internal/businesses/{business_id}/blog_posts/{blog_post_id} Update a blog post (internal) Operation id: `update_blog_post_api_v1_internal_businesses__business_id__blog_posts__blog_post_id__put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `blog_post_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): BlogPostWriteBody - `title` · string · required - `slug` · string · required - `status` · string · required - `publish_date` · string | null - `excerpt` · string | null - `content_markdown` · string | null - `content_html` · string | null - `tags` · string[] - `photo_ids` · string[] - `is_featured` · boolean - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] - `author_team_member_ids` · string[] - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null - `brief` · object | null **Responses** - `200` Successful Response: SuccessResponse_BlogPostData_ - `request_id` · string · required - `success` · true - `data` · BlogPostData · required - `id` · string · required - `business_id` · string · required - `title` · string · required - `slug` · string · required - `status` · string · required - `status_bucket` · string · required - `publish_date` · string | null - `excerpt` · string | null - `content_markdown` · string | null - `content_html` · string | null - `tags` · string[] | null - `photo_ids` · string[] | null - `is_featured` · boolean - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] | null - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null - `brief` · object | null - `author_preview` · BlogPostAuthorPreviewData[] - `authors` · BlogPostAuthorData[] - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/blog_posts/generate Generate a blog post using AI (internal) Operation id: `generate_blog_post_endpoint_api_v1_internal_businesses__business_id__blog_posts_generate_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `Idempotency-Key` | header | string \| null | no | | | `X-Workflow-Bypass` | header | string \| null | no | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): BlogPostGenerateRequest - `prompt` · string · required: User prompt describing the blog post to generate - `provider` · string | null: Optional AI provider override: openai, anthropic, gemini - `new_blog_candidate_id` · string (uuid) | null: A new-blog candidate id (blog_suggestions, scope new_post); its brief drives the post **Responses** - `200` Successful Response: SuccessResponse_BlogPostGenerateResponse_ - `request_id` · string · required - `success` · true - `data` · BlogPostGenerateResponse · required - `title` · string · required - `slug` · string · required - `excerpt` · string · required - `content_markdown` · string · required - `tags` · string[] - `media_brief` · object: Hero-photo brief for the workflow worker to resolve against the media library. Empty unless blog_media_brief_enabled is on. - `body_images` · object[]: Inline photo briefs, each paired with a [[ks-image:N]] marker in content_markdown. Empty unless blog_body_images_enabled is on. - `seo_title` · string | null - `seo_description` · string | null - `seo_keywords` · string[] - `brief` · object | null - `metadata` · BlogPostGenerateMetadata · required - `generation_status` · string - `workflow_run_id` · string | null - `thread_id` · string | null - `blog_post` · BlogPostData | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/blog/learnings Writer learnings ledger for a business (internal, debugging only) Operation id: `list_blog_learnings_api_v1_internal_businesses__business_id__blog_learnings_get` Blog writer feedback: what the writer is currently told for this business, with evidence. Internal only — the ledger is system-managed and never shown to users. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_dict__ - `request_id` · string · required - `success` · true - `data` · object[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Blog workflow engine 3 endpoints. HTML: https://developers.keystone.app/api/internal/blog-workflow-engine/ ### POST /api/v1/internal/workflow-engine/blog/attach-photos Attach Blog Photos Operation id: `attach_blog_photos_api_v1_internal_workflow_engine_blog_attach_photos_post` Attach photos to an existing draft without rewriting it. The hero is resolved after the draft is stored, and a PUT would demand title/slug/status and blank whatever it was not told. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolAttachBlogPhotosBody - `businessId` · string (uuid) · required - `blogPostId` · string (uuid) · required - `photoIds` · string (uuid)[] | null - `mediaStatus` · string | null - `workflowRunId` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/blog/review-media Review Blog Media Operation id: `review_blog_media_api_v1_internal_workflow_engine_blog_review_media_post` Vision-review hero photo candidates the worker found in the library. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolReviewBlogMediaBody - `businessId` · string (uuid) · required - `mediaBrief` · object | null - `candidates` · object[] · required - `workflowRunId` · string | null - `ranked` · boolean **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/blog/verify-hero Verify Blog Hero Operation id: `verify_blog_hero_api_v1_internal_workflow_engine_blog_verify_hero_post` Check a reframed hero before it is attached to the post. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolVerifyBlogHeroBody - `businessId` · string (uuid) · required - `mediaBrief` · object | null - `assetId` · string (uuid) · required - `intent` · object - `workflowRunId` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Consumer auth 5 endpoints. HTML: https://developers.keystone.app/api/internal/internal-consumer-auth/ ### POST /api/v1/internal/consumer-auth/complete Complete Operation id: `complete_api_v1_internal_consumer_auth_complete_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): CompleteBody - `business_id` · string (uuid) · required - `phone` · string · required - `email` · string · required - `first_name` · string · required - `last_name` · string · required - `webchat_session_id` · string | null - `client_user_agent` · string | null - `client_ip` · string | null **Responses** - `200` Successful Response: SuccessResponse_CompleteData_ - `request_id` · string · required - `success` · true - `data` · CompleteData · required - `consumer_id` · string (uuid) · required - `consumer` · ConsumerAuthProfile · required - `event_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/consumer-auth/phone-entry Phone Entry Operation id: `phone_entry_api_v1_internal_consumer_auth_phone_entry_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PhoneEntryBody - `business_id` · string (uuid) · required - `phone` · string · required **Responses** - `200` Successful Response: SuccessResponse_PhoneEntryData_ - `request_id` · string · required - `success` · true - `data` · PhoneEntryData · required - `consumer_id` · string (uuid) · required - `business_contact_id` · string (uuid) · required - `contact_was_new` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/consumer-auth/prefill Prefill Operation id: `prefill_api_v1_internal_consumer_auth_prefill_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | query | string (uuid) | yes | | | `phone` | query | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PrefillData_ - `request_id` · string · required - `success` · true - `data` · PrefillData · required - `first_name` · string | null - `last_name` · string | null - `email` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/consumer-auth/resolve-api-key Resolve Api Key Operation id: `resolve_api_key_api_v1_internal_consumer_auth_resolve_api_key_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ResolveApiKeyBody - `api_key` · string · required **Responses** - `200` Successful Response: SuccessResponse_ResolveApiKeyData_ - `request_id` · string · required - `success` · true - `data` · ResolveApiKeyData · required - `business_id` · string (uuid) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/consumers/{consumer_id}/portal-profile Consumer Portal Profile Operation id: `consumer_portal_profile_api_v1_internal_consumers__consumer_id__portal_profile_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `consumer_id` | path | string (uuid) | yes | | | `business_id` | query | string (uuid) \| null | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Contacts 45 endpoints. HTML: https://developers.keystone.app/api/internal/contacts/ ### GET /api/v1/internal/businesses/{business_id}/contacts List contacts Operation id: `list_contacts_api_v1_internal_businesses__business_id__contacts_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | | | `lifecycle_stage` | query | string \| null | no | | | `signal` | query | string \| null | no | Filter by signal: hot,warm,cool,new,unassigned | | `touch` | query | string \| null | no | Filter by touch: responsive,slow,unresponsive,ghosted,unassigned | | `stage` | query | string \| null | no | Filter by stage: new,engaged,booked,returning,lapsed,lost,unassigned | | `needs_attention` | query | boolean \| null | no | Filter contacts requiring supervisory attention | | `identity_resolution_status` | query | string \| null | no | | | `include_deleted` | query | boolean | no | | | `source` | query | string \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `updated_after` | query | string (date-time) \| null | no | | | `sort` | query | string | no | Sort order. 'last_activity' (default) surfaces the most recent conversation first; also accepts any contact column (created_at, updated_at, ...). | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactData__ - `request_id` · string · required - `success` · true - `data` · ContactData[] · required - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts Create contact Operation id: `create_contact_api_v1_internal_businesses__business_id__contacts_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ContactCreateBody - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `email` · string | null - `phone` · string | null - `emails` · ContactEmailInputBody[] - `email` · string · required - `source` · string | null - `is_primary` · boolean - `phones` · ContactPhoneInputBody[] - `phone` · string · required - `source` · string | null - `is_primary` · boolean - `status` · string - `lifecycle_stage` · string - `metadata_` · object | null - `source` · ContactSourceBody | null - `contact_source_type` · ContactSourceType · required: Allowed contact source values. - `contact_source_detail` · string | null - `external_ref_id` · string | null - `consent` · ContactConsentBody | null - `transactional_sms` · boolean | null - `marketing_sms` · boolean | null - `marketing_email` · boolean | null - `tos_privacy` · boolean | null - `ai_initiate` · boolean **Responses** - `201` Successful Response: SuccessResponse_ContactOutcomeData_ - `request_id` · string · required - `success` · true - `data` · ContactOutcomeData · required: Structured response for POST /contacts and PUT /contacts/upsert. - `contact` · ContactData · required: Contact detail / list item response shape. - `operation` · string · required - `consumer_link_status` · string · required - `duplicate_flagged` · boolean - `identity_review_id` · string | null - `duplicate_review_id` · string | null - `ai_initiation` · AiInitiationResult | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/{contact_id} Get contact detail Operation id: `get_contact_api_v1_internal_businesses__business_id__contacts__contact_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/internal/businesses/{business_id}/contacts/{contact_id} Update contact Operation id: `update_contact_api_v1_internal_businesses__business_id__contacts__contact_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ContactUpdateBody - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string | null - `lifecycle_stage` · string | null - `signal` · string | null - `touch` · string | null - `stage` · string | null - `metadata_` · object | null - `last_contacted_at` · string (date-time) | null - `auto_contact_enabled` · boolean | null - `outreach_suppressed` · boolean | null **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/internal/businesses/{business_id}/contacts/{contact_id} Delete contact Operation id: `delete_contact_api_v1_internal_businesses__business_id__contacts__contact_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/classification Override contact classification Operation id: `patch_classification_api_v1_internal_businesses__business_id__contacts__contact_id__classification_patch` Override one or more classification dimensions for the contact. Any field omitted in the body is left unchanged. Each changed dimension lands a row in `contact_classification_history` with `changed_by="user:api"` and the supplied `reason`. Manual overrides are NOT sticky — the next AI classification pass (triggered by inbound messages or DELIVERED/READ status events) may overwrite them. To preserve a manual value, take the conversation out of AUTONOMOUS AI mode. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ClassifyBody - `signal` · string | null - `touch` · string | null - `stage` · string | null - `reason` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/classification-history Get classification history Operation id: `get_classification_history_api_v1_internal_businesses__business_id__contacts__contact_id__classification_history_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `field` | query | string \| null | no | Filter by: signal, touch, stage | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ClassificationHistoryItem__ - `request_id` · string · required - `success` · true - `data` · ClassificationHistoryItem[] · required - `type` · string - `field` · string · required - `old_value` · string | null - `new_value` · string · required - `changed_by` · string · required - `reason` · string | null - `created_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/classify Manually classify a contact (legacy POST alias) Operation id: `classify_contact_api_v1_internal_businesses__business_id__contacts__contact_id__classify_post` Override one or more classification dimensions for the contact. Any field omitted in the body is left unchanged. Each changed dimension lands a row in `contact_classification_history` with `changed_by="user:api"` and the supplied `reason`. Manual overrides are NOT sticky — the next AI classification pass (triggered by inbound messages or DELIVERED/READ status events) may overwrite them. To preserve a manual value, take the conversation out of AUTONOMOUS AI mode. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ClassifyBody - `signal` · string | null - `touch` · string | null - `stage` · string | null - `reason` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/conversation Get this contact's conversation Operation id: `get_contact_conversation_api_v1_internal_businesses__business_id__contacts__contact_id__conversation_get` Returns the conversation for this contact's consumer, or `data: null` with 200 when none exists yet. 404 is reserved for the contact itself being missing from this business — the frontend should branch on `data == null`, not on status code. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_Union_ConversationData__NoneType__ - `request_id` · string · required - `success` · true - `data` · ConversationData | null · required - `id` · string · required - `consumer_id` · string | null - `status` · string · required - `assigned_to` · integer | null - `ai_enabled` · boolean - `ai_mode` · string - `last_message_at` · string (date-time) | null - `last_message_preview` · string | null - `last_message_channel` · string | null - `unread_count` · integer - `message_count` · integer - `snoozed_until` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `reply_channel` · ReplyChannelData | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/emails List contact emails Operation id: `list_emails_api_v1_internal_businesses__business_id__contacts__contact_id__emails_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactEmailData__ - `request_id` · string · required - `success` · true - `data` · ContactEmailData[] · required - `id` · string · required - `business_contact_id` · string · required - `business_id` · string · required - `raw_input` · string | null - `email` · string · required - `verified_at` · string (date-time) | null - `source` · string | null - `is_deleted` · boolean - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/emails Add email to contact Operation id: `add_email_api_v1_internal_businesses__business_id__contacts__contact_id__emails_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ContactEmailCreateBody - `email` · string · required - `source` · string | null **Responses** - `201` Successful Response: SuccessResponse_ContactEmailAddResult_ - `request_id` · string · required - `success` · true - `data` · ContactEmailAddResult · required: Response for POST /contacts/{id}/emails. - `email` · ContactEmailData · required: Email record response shape. - `identity_review_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/emails/{email_id} Update contact email Operation id: `update_email_api_v1_internal_businesses__business_id__contacts__contact_id__emails__email_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `email_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ContactEmailPatchBody - `email` · string | null - `source` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactEmailUpdateResult_ - `request_id` · string · required - `success` · true - `data` · ContactEmailUpdateResult · required: Response for PATCH /contacts/{id}/emails/{email_id}. Mirrors `ContactEmailAddResult` so the console can reuse the same response handler — `identity_review_id` is set when a value change on a consumer-linked contact opens (or finds an existing) review. - `email` · ContactEmailData · required: Email record response shape. - `identity_review_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/emails/{email_id} Remove email from contact Operation id: `remove_email_api_v1_internal_businesses__business_id__contacts__contact_id__emails__email_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `email_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/emails/{email_id}/set-primary Set primary email Operation id: `set_primary_email_api_v1_internal_businesses__business_id__contacts__contact_id__emails__email_id__set_primary_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `email_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/insights List contact insights Operation id: `list_contact_insights_api_v1_internal_businesses__business_id__contacts__contact_id__insights_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `include_dismissed` | query | boolean | no | | | `priority` | query | string \| null | no | | | `limit` | query | integer | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactInsightData__ - `request_id` · string · required - `success` · true - `data` · ContactInsightData[] · required - `id` · string · required - `contact_id` · string · required - `business_id` · string · required - `insight_type` · string · required - `title` · string · required - `body` · string · required - `priority` · string · required - `action_type` · string | null - `action_data` · object | null - `is_dismissed` · boolean - `expires_at` · string (date-time) | null - `created_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/insights/{insight_id} Get a contact insight Operation id: `get_contact_insight_api_v1_internal_businesses__business_id__contacts__contact_id__insights__insight_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `insight_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactInsightData_ - `request_id` · string · required - `success` · true - `data` · ContactInsightData · required: Contact insight response shape. - `id` · string · required - `contact_id` · string · required - `business_id` · string · required - `insight_type` · string · required - `title` · string · required - `body` · string · required - `priority` · string · required - `action_type` · string | null - `action_data` · object | null - `is_dismissed` · boolean - `expires_at` · string (date-time) | null - `created_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/insights/{insight_id}/dismiss Dismiss a contact insight Operation id: `dismiss_contact_insight_api_v1_internal_businesses__business_id__contacts__contact_id__insights__insight_id__dismiss_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `insight_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactInsightData_ - `request_id` · string · required - `success` · true - `data` · ContactInsightData · required: Contact insight response shape. - `id` · string · required - `contact_id` · string · required - `business_id` · string · required - `insight_type` · string · required - `title` · string · required - `body` · string · required - `priority` · string · required - `action_type` · string | null - `action_data` · object | null - `is_dismissed` · boolean - `expires_at` · string (date-time) | null - `created_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/needs-attention Resolve contact needs-attention overlay Operation id: `resolve_contact_needs_attention_api_v1_internal_businesses__business_id__contacts__contact_id__needs_attention_delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `re_enable_ai` | query | boolean | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: object - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/opt-in Record opt-in Operation id: `opt_in_api_v1_internal_businesses__business_id__contacts__contact_id__opt_in_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): OptInOutBody - `channel` · string · required - `source` · string · required - `occurred_at` · string (date-time) | null - `idempotency_key` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactPreferenceData_ - `request_id` · string · required - `success` · true - `data` · ContactPreferenceData · required: GET /contacts/{id}/preferences response shape. - `id` · string · required - `business_contact_id` · string · required - `transactional_sms_opt_in_at` · string (date-time) | null - `transactional_sms_opt_out_at` · string (date-time) | null - `marketing_sms_opt_in_at` · string (date-time) | null - `marketing_sms_opt_out_at` · string (date-time) | null - `marketing_email_opt_in_at` · string (date-time) | null - `marketing_email_opt_out_at` · string (date-time) | null - `tos_privacy_accepted_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/opt-out Record opt-out Operation id: `opt_out_api_v1_internal_businesses__business_id__contacts__contact_id__opt_out_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): OptInOutBody - `channel` · string · required - `source` · string · required - `occurred_at` · string (date-time) | null - `idempotency_key` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactPreferenceData_ - `request_id` · string · required - `success` · true - `data` · ContactPreferenceData · required: GET /contacts/{id}/preferences response shape. - `id` · string · required - `business_contact_id` · string · required - `transactional_sms_opt_in_at` · string (date-time) | null - `transactional_sms_opt_out_at` · string (date-time) | null - `marketing_sms_opt_in_at` · string (date-time) | null - `marketing_sms_opt_out_at` · string (date-time) | null - `marketing_email_opt_in_at` · string (date-time) | null - `marketing_email_opt_out_at` · string (date-time) | null - `tos_privacy_accepted_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/phones List contact phones Operation id: `list_phones_api_v1_internal_businesses__business_id__contacts__contact_id__phones_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactPhoneData__ - `request_id` · string · required - `success` · true - `data` · ContactPhoneData[] · required - `id` · string · required - `business_contact_id` · string · required - `business_id` · string · required - `raw_input` · string | null - `phone` · string · required - `phone_type` · string | null - `country_code` · string | null - `verified_at` · string (date-time) | null - `source` · string | null - `is_deleted` · boolean - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/phones Add phone to contact Operation id: `add_phone_api_v1_internal_businesses__business_id__contacts__contact_id__phones_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ContactPhoneCreateBody - `phone` · string · required - `source` · string | null **Responses** - `201` Successful Response: SuccessResponse_ContactPhoneAddResult_ - `request_id` · string · required - `success` · true - `data` · ContactPhoneAddResult · required: Response for POST /contacts/{id}/phones. - `phone` · ContactPhoneData · required: Phone record response shape. - `identity_review_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/phones/{phone_id} Update contact phone Operation id: `update_phone_api_v1_internal_businesses__business_id__contacts__contact_id__phones__phone_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `phone_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ContactPhonePatchBody - `phone` · string | null - `phone_type` · string | null - `country_code` · string | null - `source` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactPhoneUpdateResult_ - `request_id` · string · required - `success` · true - `data` · ContactPhoneUpdateResult · required: Response for PATCH /contacts/{id}/phones/{phone_id}. Mirrors `ContactPhoneAddResult` so the console can reuse the same response handler — `identity_review_id` is set when a value change on a consumer-linked contact opens (or finds an existing) review. - `phone` · ContactPhoneData · required: Phone record response shape. - `identity_review_id` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### DELETE /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/phones/{phone_id} Remove phone from contact Operation id: `remove_phone_api_v1_internal_businesses__business_id__contacts__contact_id__phones__phone_id__delete` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `phone_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `204` Successful Response - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/phones/{phone_id}/set-primary Set primary phone Operation id: `set_primary_phone_api_v1_internal_businesses__business_id__contacts__contact_id__phones__phone_id__set_primary_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `phone_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/preferences Get preferences Operation id: `get_preferences_api_v1_internal_businesses__business_id__contacts__contact_id__preferences_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ContactPreferenceData_ - `request_id` · string · required - `success` · true - `data` · ContactPreferenceData · required: GET /contacts/{id}/preferences response shape. - `id` · string · required - `business_contact_id` · string · required - `transactional_sms_opt_in_at` · string (date-time) | null - `transactional_sms_opt_out_at` · string (date-time) | null - `marketing_sms_opt_in_at` · string (date-time) | null - `marketing_sms_opt_out_at` · string (date-time) | null - `marketing_email_opt_in_at` · string (date-time) | null - `marketing_email_opt_out_at` · string (date-time) | null - `tos_privacy_accepted_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/reassess Trigger a fresh AI classification pass Operation id: `reassess_contact_api_v1_internal_businesses__business_id__contacts__contact_id__reassess_post` Publishes a manual `ClassificationRequest` for this contact's consumer. The classification consumer picks it up out-of-band and writes back signal/touch/stage + a fresh `latest_insight`; expect the new values to land within seconds. Returns the current contact snapshot — poll the same contact-detail endpoint to see the updated classification. Returns 202 even when Kafka is disabled — the next inbound message will pick the contact up; the response includes the current state regardless. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `202` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/score-history Get engagement score history Operation id: `get_score_history_api_v1_internal_businesses__business_id__contacts__contact_id__score_history_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ScoreHistoryItem__ - `request_id` · string · required - `success` · true - `data` · ScoreHistoryItem[] · required - `engagement_score` · integer · required - `purchase_intent` · integer | null - `model_version` · string | null - `reasoning` · string | null - `input_signals` · object | null - `created_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/sources List sources Operation id: `list_sources_api_v1_internal_businesses__business_id__contacts__contact_id__sources_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactSourceData__ - `request_id` · string · required - `success` · true - `data` · ContactSourceData[] · required - `id` · string · required - `business_contact_id` · string · required - `contact_source_type` · ContactSourceType · required: Allowed contact source values. - `contact_source_detail` · string | null - `external_ref_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/{contact_id}/sources Add source Operation id: `add_source_api_v1_internal_businesses__business_id__contacts__contact_id__sources_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `contact_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ContactSourceCreateBody - `contact_source_type` · ContactSourceType · required: Allowed contact source values. - `contact_source_detail` · string | null - `external_ref_id` · string | null **Responses** - `201` Successful Response: SuccessResponse_ContactSourceData_ - `request_id` · string · required - `success` · true - `data` · ContactSourceData · required: Source attribution response shape. - `id` · string · required - `business_contact_id` · string · required - `contact_source_type` · ContactSourceType · required: Allowed contact source values. - `contact_source_detail` · string | null - `external_ref_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/export Export contacts as CSV Operation id: `export_contacts_api_v1_internal_businesses__business_id__contacts_export_get` Stream every matching contact as a CSV file (one server request, no client-side paging). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `q` | query | string \| null | no | Optional search query; when set, matches the search view | | `status` | query | string \| null | no | | | `lifecycle_stage` | query | string \| null | no | | | `signal` | query | string \| null | no | | | `touch` | query | string \| null | no | | | `stage` | query | string \| null | no | | | `needs_attention` | query | boolean \| null | no | | | `identity_resolution_status` | query | string \| null | no | | | `include_deleted` | query | boolean | no | | | `source` | query | string \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `updated_after` | query | string (date-time) \| null | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/lookup Lookup contact by email/phone Operation id: `lookup_contacts_api_v1_internal_businesses__business_id__contacts_lookup_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ContactLookupBody - `email` · string | null - `phone` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactLookupResult_ - `request_id` · string · required - `success` · true - `data` · ContactLookupResult · required: POST /contacts/lookup response data. - `match_type` · string · required - `contacts` · ContactData[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/contacts/merge Merge contacts Operation id: `merge_contacts_api_v1_internal_businesses__business_id__contacts_merge_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ContactMergeBody - `surviving_contact_id` · string (uuid) · required - `merged_contact_id` · string (uuid) · required - `field_resolutions` · object | null - `merge_reason` · string | null **Responses** - `200` Successful Response: SuccessResponse_ContactData_ - `request_id` · string · required - `success` · true - `data` · ContactData · required: Contact detail / list item response shape. - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/needs-attention List contacts needing attention Operation id: `get_needs_attention_api_v1_internal_businesses__business_id__contacts_needs_attention_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `limit` | query | integer | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_NeedsAttentionData_ - `request_id` · string · required - `success` · true - `data` · NeedsAttentionData · required: GET /contacts/needs-attention response shape. - `items` · NeedsAttentionItem[] · required - `total` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/pipeline-summary Get pipeline summary Operation id: `get_pipeline_summary_api_v1_internal_businesses__business_id__contacts_pipeline_summary_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_PipelineSummaryData_ - `request_id` · string · required - `success` · true - `data` · PipelineSummaryData · required: GET /contacts/pipeline-summary response shape. - `summary` · string · required - `stats` · object · required - `generated_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/search Search contacts Operation id: `search_contacts_api_v1_internal_businesses__business_id__contacts_search_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `q` | query | string | yes | | | `status` | query | string \| null | no | | | `lifecycle_stage` | query | string \| null | no | | | `signal` | query | string \| null | no | Filter by signal: hot,warm,cool,new,unassigned | | `touch` | query | string \| null | no | Filter by touch: responsive,slow,unresponsive,ghosted,unassigned | | `stage` | query | string \| null | no | Filter by stage: new,engaged,booked,returning,lapsed,lost,unassigned | | `needs_attention` | query | boolean \| null | no | Filter contacts requiring supervisory attention | | `identity_resolution_status` | query | string \| null | no | | | `include_deleted` | query | boolean | no | | | `source` | query | string \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `updated_after` | query | string (date-time) \| null | no | | | `sort` | query | string | no | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_ContactData__ - `request_id` · string · required - `success` · true - `data` · ContactData[] · required - `id` · string · required - `business_id` · string · required - `consumer_id` · string | null - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `status` · string · required - `lifecycle_stage` · string · required - `identity_resolution_status` · string · required - `signal` · string - `touch` · string - `stage` · string - `engagement_score` · integer | null - `purchase_intent` · integer | null - `primary_email` · string | null - `primary_phone` · string | null - `last_contacted_at` · string (date-time) | null - `last_replied_at` · string (date-time) | null - `last_inbound_at` · string (date-time) | null - `last_outbound_at` · string (date-time) | null - `needs_attention` · boolean - `needs_attention_reason` · string | null - `needs_attention_at` · string (date-time) | null - `metadata_` · object - `is_deleted` · boolean - `deleted_at` · string (date-time) | null - `merged_into_contact_id` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `first_source_type` · string | null - `first_source_detail` · string | null - `first_source_message` · string | null - `latest_insight` · LatestInsightSummary | null - `conversation_id` · string | null - `message_count` · integer - `auto_contact_enabled` · boolean - `outreach_suppressed` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/contacts/stats Get classification stats Operation id: `get_classification_stats_api_v1_internal_businesses__business_id__contacts_stats_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | | | `lifecycle_stage` | query | string \| null | no | | | `signal` | query | string \| null | no | | | `touch` | query | string \| null | no | | | `stage` | query | string \| null | no | | | `needs_attention` | query | boolean \| null | no | | | `identity_resolution_status` | query | string \| null | no | | | `source` | query | string \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `updated_after` | query | string (date-time) \| null | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ClassificationStatsData_ - `request_id` · string · required - `success` · true - `data` · ClassificationStatsData · required: GET /contacts/stats response shape. - `total` · integer · required - `signal` · object · required - `touch` · object · required - `stage` · object · required - `lifecycle_stage` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PUT /api/v1/internal/businesses/{business_id}/contacts/upsert Upsert contact Operation id: `upsert_contact_api_v1_internal_businesses__business_id__contacts_upsert_put` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ContactCreateBody - `first_name` · string | null - `last_name` · string | null - `display_name` · string | null - `email` · string | null - `phone` · string | null - `emails` · ContactEmailInputBody[] - `email` · string · required - `source` · string | null - `is_primary` · boolean - `phones` · ContactPhoneInputBody[] - `phone` · string · required - `source` · string | null - `is_primary` · boolean - `status` · string - `lifecycle_stage` · string - `metadata_` · object | null - `source` · ContactSourceBody | null - `contact_source_type` · ContactSourceType · required: Allowed contact source values. - `contact_source_detail` · string | null - `external_ref_id` · string | null - `consent` · ContactConsentBody | null - `transactional_sms` · boolean | null - `marketing_sms` · boolean | null - `marketing_email` · boolean | null - `tos_privacy` · boolean | null - `ai_initiate` · boolean **Responses** - `200` Successful Response: SuccessResponse_ContactOutcomeData_ - `request_id` · string · required - `success` · true - `data` · ContactOutcomeData · required: Structured response for POST /contacts and PUT /contacts/upsert. - `contact` · ContactData · required: Contact detail / list item response shape. - `operation` · string · required - `consumer_link_status` · string · required - `duplicate_flagged` · boolean - `identity_review_id` · string | null - `duplicate_review_id` · string | null - `ai_initiation` · AiInitiationResult | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/duplicate-reviews List duplicate reviews Operation id: `list_duplicate_reviews_api_v1_internal_businesses__business_id__duplicate_reviews_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_DuplicateReviewData__ - `request_id` · string · required - `success` · true - `data` · DuplicateReviewData[] · required - `id` · string · required - `business_id` · string · required - `contact_id_a` · string · required - `contact_id_b` · string · required - `match_type` · string · required - `confidence_score` · number | null - `status` · string · required - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact_a` · ContactSummary | null - `contact_b` · ContactSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/duplicate-reviews/{review_id} Get duplicate review Operation id: `get_duplicate_review_api_v1_internal_businesses__business_id__duplicate_reviews__review_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_DuplicateReviewData_ - `request_id` · string · required - `success` · true - `data` · DuplicateReviewData · required: Duplicate review response shape. - `id` · string · required - `business_id` · string · required - `contact_id_a` · string · required - `contact_id_b` · string · required - `match_type` · string · required - `confidence_score` · number | null - `status` · string · required - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact_a` · ContactSummary | null - `contact_b` · ContactSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/duplicate-reviews/{review_id}/dismiss Dismiss duplicate pair Operation id: `dismiss_duplicate_pair_api_v1_internal_businesses__business_id__duplicate_reviews__review_id__dismiss_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): DuplicateReviewDismissBody - `reason` · string | null **Responses** - `200` Successful Response: SuccessResponse_DuplicateReviewData_ - `request_id` · string · required - `success` · true - `data` · DuplicateReviewData · required: Duplicate review response shape. - `id` · string · required - `business_id` · string · required - `contact_id_a` · string · required - `contact_id_b` · string · required - `match_type` · string · required - `confidence_score` · number | null - `status` · string · required - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact_a` · ContactSummary | null - `contact_b` · ContactSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/duplicate-reviews/{review_id}/merge Merge duplicate pair Operation id: `merge_duplicate_pair_api_v1_internal_businesses__business_id__duplicate_reviews__review_id__merge_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): DuplicateReviewMergeBody - `surviving_contact_id` · string · required - `field_resolutions` · object | null **Responses** - `200` Successful Response: SuccessResponse_DuplicateReviewData_ - `request_id` · string · required - `success` · true - `data` · DuplicateReviewData · required: Duplicate review response shape. - `id` · string · required - `business_id` · string · required - `contact_id_a` · string · required - `contact_id_b` · string · required - `match_type` · string · required - `confidence_score` · number | null - `status` · string · required - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact_a` · ContactSummary | null - `contact_b` · ContactSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/identity-reviews List identity reviews Operation id: `list_identity_reviews_api_v1_internal_businesses__business_id__identity_reviews_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `status` | query | string \| null | no | | | `business_contact_id` | query | string (uuid) \| null | no | | | `email_matched_consumer_id` | query | string (uuid) \| null | no | | | `phone_matched_consumer_id` | query | string (uuid) \| null | no | | | `created_after` | query | string (date-time) \| null | no | | | `created_before` | query | string (date-time) \| null | no | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_list_IdentityReviewData__ - `request_id` · string · required - `success` · true - `data` · IdentityReviewData[] · required - `id` · string · required - `business_contact_id` · string · required - `email_matched_consumer_id` · string | null - `phone_matched_consumer_id` · string | null - `conflict_snapshot` · object · required - `status` · string · required - `resolved_consumer_id` · string | null - `resolution_action` · string | null - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `notes` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact` · ContactSummary | null - `email_matched_consumer` · ConsumerSummary | null - `phone_matched_consumer` · ConsumerSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/identity-reviews/{review_id} Get identity review Operation id: `get_identity_review_api_v1_internal_businesses__business_id__identity_reviews__review_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_IdentityReviewData_ - `request_id` · string · required - `success` · true - `data` · IdentityReviewData · required: Identity review response shape. - `id` · string · required - `business_contact_id` · string · required - `email_matched_consumer_id` · string | null - `phone_matched_consumer_id` · string | null - `conflict_snapshot` · object · required - `status` · string · required - `resolved_consumer_id` · string | null - `resolution_action` · string | null - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `notes` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact` · ContactSummary | null - `email_matched_consumer` · ConsumerSummary | null - `phone_matched_consumer` · ConsumerSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/identity-reviews/{review_id}/resolve Resolve identity review Operation id: `resolve_identity_review_api_v1_internal_businesses__business_id__identity_reviews__review_id__resolve_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `review_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): IdentityReviewResolveBody - `action` · "link_to_consumer" | "merge_consumers_and_link" | "create_new_consumer" | "ignore" · required - `target_consumer_id` · string | null - `notes` · string | null **Responses** - `200` Successful Response: SuccessResponse_IdentityReviewData_ - `request_id` · string · required - `success` · true - `data` · IdentityReviewData · required: Identity review response shape. - `id` · string · required - `business_contact_id` · string · required - `email_matched_consumer_id` · string | null - `phone_matched_consumer_id` · string | null - `conflict_snapshot` · object · required - `status` · string · required - `resolved_consumer_id` · string | null - `resolution_action` · string | null - `resolved_by` · string | null - `resolved_at` · string (date-time) | null - `notes` · string | null - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `contact` · ContactSummary | null - `email_matched_consumer` · ConsumerSummary | null - `phone_matched_consumer` · ConsumerSummary | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Data models 2 endpoints. HTML: https://developers.keystone.app/api/internal/internal-data-models/ ### GET /api/v1/internal/businesses/{business_id}/data-models List mutable business data models (workflow catalog) Operation id: `list_data_models_api_v1_internal_businesses__business_id__data_models_get` Return catalog of entity types the agent workflow may update. Validates business exists. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_DataModelCatalogData_ - `request_id` · string · required - `success` · true - `data` · DataModelCatalogData · required: GET /internal/businesses/{id}/data-models response data. - `business_id` · string · required - `models` · DataModelCatalogEntry[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/data-models/{entity}/update-schema JSON Schema for create/update bodies of a business data entity Operation id: `get_update_schema_api_v1_internal_businesses__business_id__data_models__entity__update_schema_get` Return Pydantic-derived JSON Schemas for the entity's create and update bodies. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `entity` | path | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_DataModelUpdateSchemaData_ - `request_id` · string · required - `success` · true - `data` · DataModelUpdateSchemaData · required: GET .../data-models/{entity}/update-schema response data. - `entity_id` · string · required - `display_name` · string · required - `requires_target_identifier_for_modify_delete` · boolean - `target_identifier_hint` · string | null - `create_body_json_schema` · object · required - `update_body_json_schema` · object · required - `fields_summary` · FieldSummaryRow[] · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Follow ups 1 endpoints. HTML: https://developers.keystone.app/api/internal/follow-ups/ ### POST /api/v1/internal/follow-ups/execute Execute Follow Up Operation id: `execute_follow_up_api_v1_internal_follow_ups_execute_post` Kairos callback for a previously scheduled follow-up. Inserts an OUTBOUND message and publishes it through the standard outbound flow. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): FollowUpExecuteBody - `conversation_id` · string (uuid) · required - `consumer_id` · string (uuid) · required - `triggering_message_id` · string (uuid) · required **Responses** - `200` Successful Response: SuccessResponse_FollowUpExecuteData_ - `request_id` · string · required - `success` · true - `data` · FollowUpExecuteData · required - `message_id` · string (uuid) | null - `skipped` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Import data 1 endpoints. HTML: https://developers.keystone.app/api/internal/import-data-internal/ ### POST /api/v1/internal/import-data/scrape-media-backfill Promote one bounded batch of scraped image assets into media libraries Operation id: `scrape_media_backfill_api_v1_internal_import_data_scrape_media_backfill_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `max_assets` | query | integer | no | | | `business_id` | query | string (uuid) \| null | no | Restrict the sweep to one business | | `dry_run` | query | boolean | no | Report counts only; no copies or writes | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ScrapeMediaBackfillResult_ - `request_id` · string · required - `success` · true - `data` · ScrapeMediaBackfillResult · required: One bounded sweep of the scrape-asset → media-library backfill (KS-1613). Invariant: ``processed == promoted + skipped_duplicate + skipped_copy_failed``. ``remaining`` counts still-eligible assets after this call — the caller's loop driver. ``unpromotable_unconfigured`` is informational (assets whose blob was never stored; they are never processed). - `processed` · integer · required - `promoted` · integer · required - `skipped_duplicate` · integer · required - `skipped_copy_failed` · integer · required - `remaining` · integer · required - `unpromotable_unconfigured` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Media 8 endpoints. HTML: https://developers.keystone.app/api/internal/media-internal/ ### GET /api/v1/internal/media/businesses Businesses with >=1 live photo — media-service bulk-backfill enumeration (internal) Operation id: `list_media_businesses_api_v1_internal_media_businesses_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: InternalMediaBusinessListData - `businesses` · InternalMediaBusinessItem[] · required - `business_id` · string · required - `photo_count` · integer · required - `next_cursor` · string | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/media/businesses/{business_id}/members/{user_id} Is this heimdal user a member of this business? — media-service tenant check (internal) Operation id: `get_media_business_membership_api_v1_internal_media_businesses__business_id__members__user_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `user_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: BusinessMembershipData - `business_id` · string (uuid) · required - `user_id` · string (uuid) · required - `is_member` · boolean · required - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/media/gallery Paged industry-gallery list for the media-service industry sweep (internal) Operation id: `list_media_gallery_api_v1_internal_media_gallery_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `industry_id` | query | string \| null | no | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: InternalGalleryPhotoListData - `items` · InternalGalleryPhotoItem[] · required - `asset_id` · string · required - `industry_id` · string | null - `photo_source` · string - `public_url` · string | null - `storage` · MediaStorageRef | null - `mime_type` · string | null - `width_px` · integer | null - `height_px` · integer | null - `created_at` · string | null - `next_cursor` · string | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/media/gallery/{photo_id}/bytes Stream one industry-gallery photo's blob (internal) Operation id: `get_media_gallery_photo_bytes_api_v1_internal_media_gallery__photo_id__bytes_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `photo_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/media/photos Paged photo list for media-service ingest/backfill/sweep (internal) Operation id: `list_media_photos_api_v1_internal_media_photos_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | query | string (uuid) | yes | | | `cursor` | query | string \| null | no | | | `limit` | query | integer | no | | | `include_deleted` | query | boolean | no | | | `updated_after` | query | string \| null | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: InternalMediaPhotoListData - `items` · InternalMediaPhotoItem[] · required - `asset_id` · string · required - `business_id` · string · required - `photo_source` · string · required - `public_url` · string | null - `storage` · MediaStorageRef | null - `external` · MediaExternalRef | null - `mime_type` · string | null - `width_px` · integer | null - `height_px` · integer | null - `is_deleted` · boolean - `created_at` · string | null - `updated_at` · string | null - `next_cursor` · string | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/media/photos/{photo_id}/bytes Stream one photo's blob (internal); 410 when an external URL expired Operation id: `get_media_photo_bytes_api_v1_internal_media_photos__photo_id__bytes_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `photo_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/media/photos/{photo_id}/usages Delete-guard cross-check: modules referencing this photo (internal) Operation id: `get_media_photo_usages_api_v1_internal_media_photos__photo_id__usages_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `photo_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: PhotoUsagesData - `asset_id` · string · required - `in_use` · boolean · required - `usages` · PhotoUsageItem[] · required - `module` · string · required - `label` · string · required - `count` · integer · required - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/media/photos/import-derivative Write an edited image back as a first-class derived photo (internal) Operation id: `import_media_derivative_api_v1_internal_media_photos_import_derivative_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (multipart/form-data, required): Body_import_media_derivative_api_v1_internal_media_photos_import_derivative_post - `file` · string · required - `meta` · string · required **Responses** - `201` Successful Response: ImportDerivativeData - `asset_id` · string · required - `public_url` · string | null - `photo_source` · string - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Notifications 1 endpoints. HTML: https://developers.keystone.app/api/internal/notifications/ ### POST /api/v1/internal/{provider}/notifications Handle Listing Provider Notifications Operation id: `handle_listing_provider_notifications_api_v1_internal__provider__notifications_post` Handle listing provider push envelope (e.g. Pub/Sub OIDC ``Authorization: Bearer``). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `provider` | path | ListingProvider | yes | | | `authorization` | header | string \| null | no | | **Request body** (application/json, required): object **Responses** - `200` Successful Response: SuccessResponse_ListingNotificationHandleResponseData_ - `request_id` · string · required - `success` · true - `data` · ListingNotificationHandleResponseData · required: Response data for ``POST /{provider}/notifications`` (push ingress). - `result` · string · required - `enqueued` · boolean · required - `reason` · string | null - `event` · object · required - `resolved` · object | object[] | null - `failures` · object[] | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Onboarding 21 endpoints. HTML: https://developers.keystone.app/api/internal/onboarding-internal/ ### POST /api/v1/internal/workflow-engine/onboarding/crawl-attempts/{attempt_id}/assets Ingest Assets Operation id: `ingest_assets_api_v1_internal_workflow_engine_onboarding_crawl_attempts__attempt_id__assets_post` Checkpointed batch of downloaded+stored assets (metadata + GCS blob name + DOM signals). Idempotent by (crawl_attempt_id, asset_url). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `attempt_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): IngestAssetsBody - `assets` · IngestAssetItem[] - `asset_url` · string · required - `asset_type` · string · required - `page_id` · string | null - `asset_host` · string | null - `mime_type` · string | null - `alt_text` · string | null - `source_page_url` · string | null - `width` · integer | null - `height` · integer | null - `byte_size` · integer | null - `checksum_sha256` · string | null - `download_status` · string | null - `download_error` · string | null - `storage_status` · string | null - `storage_error` · string | null - `storage_provider` · string | null - `storage_bucket` · string | null - `storage_blob_name` · string | null - `dom_signals` · object | null **Responses** - `200` Successful Response: SuccessResponse_IngestAssetsData_ - `request_id` · string · required - `success` · true - `data` · IngestAssetsData · required - `created` · integer - `updated` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/crawl-attempts/{attempt_id}/classify Classify Crawl Pages Operation id: `classify_crawl_pages_api_v1_internal_workflow_engine_onboarding_crawl_attempts__attempt_id__classify_post` Label every crawled page and return the fan-out routing table (W3). One cheap batched LLM call over compact page descriptors. The response is the engine's whole work list: ``batches`` (one extract-domain call each), ``blog_post_page_ids`` (one extract-blog-post call each) and ``other_page_ids``. Every page appears in at least one of the three — a page the classifier missed is assigned ``other`` and reported in ``unrouted_page_ids``, never dropped. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `attempt_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_ClassifyPagesData_ - `request_id` · string · required - `success` · true - `data` · ClassifyPagesData · required - `routing` · object - `batches` · ClassifyBatchData[] - `blog_post_page_ids` · string[] - `other_page_ids` · string[] - `unrouted_page_ids` · string[] - `page_count` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/crawl-attempts/{attempt_id}/complete Complete Crawl Operation id: `complete_crawl_api_v1_internal_workflow_engine_onboarding_crawl_attempts__attempt_id__complete_post` Finalize the crawl attempt. On ``completed`` runs media promotion inline (scoped to this attempt) so scraped images enter the photo library. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `attempt_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): CompleteCrawlBody - `status` · string · required - `counters` · CompleteCrawlCounters | null - `pages_scraped` · integer | null - `pages_succeeded` · integer | null - `pages_failed` · integer | null - `assets_discovered` · integer | null - `summary_json` · object | null - `error_message` · string | null **Responses** - `200` Successful Response: SuccessResponse_CompleteCrawlData_ - `request_id` · string · required - `success` · true - `data` · CompleteCrawlData · required - `status` · string · required - `promoted` · integer - `media_promoted` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/crawl-attempts/{attempt_id}/extract-blog-post Extract Blog Post Fragment Operation id: `extract_blog_post_fragment_api_v1_internal_workflow_engine_onboarding_crawl_attempts__attempt_id__extract_blog_post_post` Extract one blog post from one page (W5). The finest fan-out grain: one call per post is what restores full article bodies. The engine treats a post that exhausts its retries as best-effort — it drops the fragment and merge's CSS fallback covers the post. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `attempt_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ExtractBlogPostBody - `page_id` · string · required **Responses** - `200` Successful Response: SuccessResponse_ExtractBlogPostData_ - `request_id` · string · required - `success` · true - `data` · ExtractBlogPostData · required - `page_id` · string · required - `fragment` · object - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/crawl-attempts/{attempt_id}/extract-domain Extract Domain Fragment Operation id: `extract_domain_fragment_api_v1_internal_workflow_engine_onboarding_crawl_attempts__attempt_id__extract_domain_post` Extract one routed batch (≤5 pages) for one data domain (W4). The batch's full page content plus the domain's current-SOR slice go into one LLM call whose whole output budget belongs to this batch. Returns a payload-shaped fragment — off-domain collections included, because an FAQ sitting on a service page must survive a classifier mislabel. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `attempt_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ExtractDomainBody - `domain` · string · required - `page_ids` · string[] **Responses** - `200` Successful Response: SuccessResponse_ExtractDomainData_ - `request_id` · string · required - `success` · true - `data` · ExtractDomainData · required - `domain` · string · required - `page_ids` · string[] - `fragment` · object - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/crawl-attempts/{attempt_id}/extract-findings Extract Findings Operation id: `extract_findings_api_v1_internal_workflow_engine_onboarding_crawl_attempts__attempt_id__extract_findings_post` Sweep every crawled page for off-schema value → typed ``extra:*`` contexts (W6). Additive by design: an empty crawl returns an empty list rather than failing, and malformed entries are dropped instead of costing the good ones. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `attempt_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ExtractFindingsBody - `captured_domains` · string[] **Responses** - `200` Successful Response: SuccessResponse_ExtractFindingsData_ - `request_id` · string · required - `success` · true - `data` · ExtractFindingsData · required - `contexts` · TypedContextData[] - `pages_reviewed` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/workflow-engine/onboarding/crawl-attempts/{attempt_id}/fetched-urls Fetched Urls Operation id: `fetched_urls_api_v1_internal_workflow_engine_onboarding_crawl_attempts__attempt_id__fetched_urls_get` Normalized URLs already persisted for this attempt — the scrape job seeds its visited-set from this so a retry skips completed pages. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `attempt_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_FetchedUrlsData_ - `request_id` · string · required - `success` · true - `data` · FetchedUrlsData · required - `urls` · string[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/crawl-attempts/{attempt_id}/heartbeat Heartbeat Crawl Attempt Operation id: `heartbeat_crawl_attempt_api_v1_internal_workflow_engine_onboarding_crawl_attempts__attempt_id__heartbeat_post` Proof of life from the scrape job, independent of ingest traffic. Pages reach sor per batch and assets only after the crawl, so a job can be busy for longer than the stale window without calling ``/pages`` or ``/assets`` — most plainly a retry that re-crawls the pages a killed execution already persisted. The job beats this on a timer for the whole crawl + asset pass. No-op (``beat=false``) once the attempt is terminal, so a superseded execution cannot keep a finished run looking alive. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `attempt_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CrawlHeartbeatData_ - `request_id` · string · required - `success` · true - `data` · CrawlHeartbeatData · required: ``beat`` is False when the attempt is already terminal: a late beat from a superseded execution must not keep the run looking alive. - `beat` · boolean · required - `last_heartbeat_at` · integer | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/workflow-engine/onboarding/crawl-attempts/{attempt_id}/pages List Pages Operation id: `list_pages_api_v1_internal_workflow_engine_onboarding_crawl_attempts__attempt_id__pages_get` Pages of a crawl attempt, in one of two shapes (design §11). ``fields=descriptor`` (default): compact classifier descriptors — url, title, H1/H2 headings, first-500-char excerpt. Plain parsing, no LLM. ``fields=content``: full untruncated page text for the extractors, from the same source the legacy extraction path reads (parity contract). NOTE: ``fields=content`` has NO consumer in the pipeline any more. It existed only to ship page text to the engine's extraction activities; those now run inside sor (``/classify``, ``/extract-domain``, …) and read the pages straight from the repository. Kept for ad-hoc inspection pending a decision on removing it. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `attempt_id` | path | string (uuid) | yes | | | `fields` | query | string | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_Union_PageDescriptorsData__PageContentsData__ - `request_id` · string · required - `success` · true - `data` · PageDescriptorsData | PageContentsData · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/crawl-attempts/{attempt_id}/pages Ingest Pages Operation id: `ingest_pages_api_v1_internal_workflow_engine_onboarding_crawl_attempts__attempt_id__pages_post` Checkpointed batch of crawled pages. Idempotent via the (crawl_attempt_id, normalized_url) unique constraint. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `attempt_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): IngestPagesBody - `pages` · IngestPageItem[] - `url` · string · required - `normalized_url` · string · required - `title` · string | null - `http_status` · integer | null - `content_type` · string | null - `depth` · integer - `markdown` · string | null - `cleaned_html` · string | null - `raw_text` · string | null - `metadata_json` · object | null - `links_json` · object | null - `images_json` · object[] | null - `crawl_error` · string | null **Responses** - `200` Successful Response: SuccessResponse_IngestPagesData_ - `request_id` · string · required - `success` · true - `data` · IngestPagesData · required - `created` · integer - `updated` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/internal/workflow-engine/onboarding/photos/tags Update Photo Tags Operation id: `update_photo_tags_api_v1_internal_workflow_engine_onboarding_photos_tags_patch` Merge vision categories into ``photo_metadata.tags`` (S5 / design §13 [3]). Set-union per photo — tags an admin or an earlier pass added are never dropped. Unknown (or deleted) photo_ids are skipped and reported rather than 404-ing the batch: the categoriser's write must land for every photo that still exists. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PhotoTagsBody - `updates` · PhotoTagUpdateItem[] - `photo_id` · string (uuid) · required - `tags` · string[] **Responses** - `200` Successful Response: SuccessResponse_PhotoTagsData_ - `request_id` · string · required - `success` · true - `data` · PhotoTagsData · required - `updated` · integer - `skipped` · string[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/runs/{run_id}/assemble Assemble Run Payload Operation id: `assemble_run_payload_api_v1_internal_workflow_engine_onboarding_runs__run_id__assemble_post` Assemble every fan-out fragment into one payload and merge it (W6). Concatenation only — dedup is merge planning's job. Routes through the same ``apply_assembled_merge`` as ``/runs/{run_id}/merge`` so the fan-out never grows a second promotion path; provenance is stamped ``website scrape fanout``. Response data is the merge counts summary. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `run_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): AssemblePayloadBody - `fragments` · object[] - `contexts` · object[] - `extract_attempt_id` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/runs/{run_id}/categorise-images Categorise Run Images Operation id: `categorise_run_images_api_v1_internal_workflow_engine_onboarding_runs__run_id__categorise_images_post` Categorise the run's promoted images and auto-assign the logo slots (W7). One deterministic logo shortlist + one capped multimodal LLM call + two writes (tags merged, slots filled only-if-empty). The engine is a thin orchestrator over this call: all the vision logic and every guardrail — the shortlist-only logo pick, the >=180px favicon floor, the never-clobber slot rule — live in sor, next to the photo data they judge. Takes no body. Same 200-with-``status`` contract as ``/stages/{stage}``: a run with no photos comes back ``skipped`` (the LLM is never called) and a domain failure comes back ``failed`` for the engine to interpret, while infra faults — and an LLM reply that should simply be re-asked — 5xx so Temporal retries them. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `run_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_CategoriseImagesData_ - `request_id` · string · required - `success` · true - `data` · CategoriseImagesData · required: Outcome of the WS3 logo-detection pass (design §13). ``status`` is the pass's own outcome, not the HTTP outcome: "completed", "skipped" (nothing to do — see ``reason``) or "failed" (a domain failure the engine treats per its own fatality policy; infra faults 5xx instead). - `status` · string - `reason` · string | null - `photos` · integer - `attached` · integer - `tags_updated` · integer - `shortlist` · string[] - `logo` · string | null - `favicon` · string | null - `assigned` · object - `skipped` · object - `error` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/runs/{run_id}/crawl-attempts Create Crawl Attempt Operation id: `create_crawl_attempt_api_v1_internal_workflow_engine_onboarding_runs__run_id__crawl_attempts_post` Open a crawl attempt for a run. Idempotent by natural key: if the run already has a *running* attempt (a retried CreateCrawlAttempt activity), it is returned instead of opening a second one. ``Idempotency-Key`` is accepted for tracing/forward-compat but the running-attempt check is the guard. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `run_id` | path | string (uuid) | yes | | | `Idempotency-Key` | header | string \| null | no | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): CreateCrawlAttemptBody - `crawler_provider` · string **Responses** - `200` Successful Response: SuccessResponse_CreateCrawlAttemptData_ - `request_id` · string · required - `success` · true - `data` · CreateCrawlAttemptData · required - `crawl_attempt_id` · string · required - `reused` · boolean - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/runs/{run_id}/enrich Enrich Run Operation id: `enrich_run_api_v1_internal_workflow_engine_onboarding_runs__run_id__enrich_post` Run WS4 enrichment for one run (design §14) — the whole pass in one call. Deterministic quality gate over the run's business → (only when it flags something) one fact-locked LLM call → partial payload through ``apply_assembled_merge`` with ``source_kind: "enrichment"``. When the gate flags nothing and the FAQ count is already sufficient the LLM is never called and the response is ``status="skipped"``. No request body and no idempotency key: the gate re-reads current SOR state every call, so an already-enriched business is simply no longer thin and a repeat invocation short-circuits. That is what makes this endpoint safe for the console to expose as a button later — a fixed per-run key would instead swallow a deliberate second pass. Failure split matches ``/stages/{stage}``: a stage-domain failure returns **200** with ``status="failed"`` so the engine applies its own fatality policy (enrichment is best-effort), while infra faults — and a malformed LLM reply, which is worth re-asking — 5xx so Temporal retries. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `run_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_EnrichRunData_ - `request_id` · string · required - `success` · true - `data` · EnrichRunData · required: What the enrichment pass did — gate verdict first, merge result second. ``status``: ``skipped`` when the gate flagged nothing (the LLM was never called), ``completed`` when the pass ran, ``failed`` for a stage-domain failure the caller owns the policy for (infra faults 5xx instead). - `status` · string · required - `flagged` · integer - `flagged_fields` · string[] - `reasons` · object - `faq_gap` · integer - `llm_called` · boolean - `enriched` · integer - `faqs_added` · integer - `merge` · object | null - `error` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/runs/{run_id}/merge Merge Run Operation id: `merge_run_api_v1_internal_workflow_engine_onboarding_runs__run_id__merge_post` Merge an engine-assembled extraction payload into the SOR (S4). The single promotion boundary: the WS2 fan-out assembler and WS4 enrichment land through the same ``merge_payload`` the legacy chain uses. Typed ``contexts`` list entries (``extra:``) each become their own business_context_versions row. Response data is the merge counts summary. Failures 5xx on purpose (unlike ``/stages/{stage}``): the merge activity's Temporal retry policy treats transport/5xx as retryable and 4xx as fatal, which is exactly the split a planner-LLM hiccup vs a bad payload needs. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `run_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): MergeRunBody - `payload` · object · required - `provenance` · MergeProvenance · required - `source_kind` · string · required - `extract_attempt_id` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/workflow-engine/onboarding/runs/{run_id}/photos List Run Photos Operation id: `list_run_photos_api_v1_internal_workflow_engine_onboarding_runs__run_id__photos_get` Read-only compact list of the run's promoted photos (scrape-source, live) — the same projection the in-sor categoriser scores, exposed for inspection and for any caller that wants the raw inventory. See ``image_categorisation.run_photo_view`` for the shape. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `run_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_RunPhotosData_ - `request_id` · string · required - `success` · true - `data` · RunPhotosData · required - `photos` · RunPhotoData[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/workflow-engine/onboarding/runs/{run_id}/sor-state Get Sor State Operation id: `get_sor_state_api_v1_internal_workflow_engine_onboarding_runs__run_id__sor_state_get` Read-only compact slice of the business's current SOR state for one extraction domain (design §11), so a WS2/WS4 extractor sees what already exists and its output dedups well at merge time. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `run_id` | path | string (uuid) | yes | | | `domain` | query | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SorStateData_ - `request_id` · string · required - `success` · true - `data` · SorStateData · required - `domain` · string · required - `state` · object - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/runs/{run_id}/stage-status Record Stage Status Operation id: `record_stage_status_api_v1_internal_workflow_engine_onboarding_runs__run_id__stage_status_post` Project a workflow stage transition into ``onboarding_json.stages``. The admin console's onboarding checklist reads the same shape whether the run was driven by the legacy in-process chain or the engine; ``executor`` and ``workflow_run_id`` are what distinguish them. The stage key is not validated against a fixed list so later workstreams (classification, enrichment) can journal new stages without a sor deploy. The body is merged over the stage's existing entry, not substituted for it. ``/stages/{stage}`` and ``/stage-status`` are two calls, and keys the wrapper produced must survive the second one even if the engine doesn't echo them: ``finalize_website_onboarding`` finds the run by the ``website_id`` the website stage journaled, so dropping it would strand that stage at ``running`` forever. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `run_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): StageStatusBody - `stage` · string · required - `status` · string · required - `error` · string | null - `workflow_run_id` · string | null - `outcome` · object | null **Responses** - `200` Successful Response: SuccessResponse_StageStatusData_ - `request_id` · string · required - `success` · true - `data` · StageStatusData · required - `stage` · string · required - `status` · string · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/runs/{run_id}/stages/{stage} Run Stage Operation id: `run_stage_api_v1_internal_workflow_engine_onboarding_runs__run_id__stages__stage__post` Run one onboarding stage synchronously. V1 of the engine-owned pipeline wraps the existing sor stage implementations rather than reimplementing them, so the legacy chain and the workflow drive identical code. A stage-level failure returns **200** with ``status="failed"`` and the error: the workflow engine owns retry and fatality policy (merge/website are fatal, company_name/industries are not), so it must see the outcome rather than a 5xx it would blindly retry. Genuine infrastructure faults still 5xx. Deliberately does not journal — the engine is the single writer of ``onboarding_json.stages`` via ``/stage-status``. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `run_id` | path | string (uuid) | yes | | | `stage` | path | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): RunStageBody - `extract_attempt_id` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_RunStageData_ - `request_id` · string · required - `success` · true - `data` · RunStageData · required - `stage` · string · required - `status` · string · required - `outcome` · object - `error` · string | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/onboarding/runs/{run_id}/website-photos/auto-assign Auto Assign Website Photos Operation id: `auto_assign_website_photos_api_v1_internal_workflow_engine_onboarding_runs__run_id__website_photos_auto_assign_post` Fill the logo/favicon ``website_photos`` slots — only where empty. An admin (or earlier-run) assignment always wins: a filled slot is reported ``already_set`` and never clobbered (design §13 guardrails). The website is resolved through the run's business. A photo_id that isn't a live photo of that business is skipped as ``unknown_photo`` so a bad id can't wedge a slot with a dangling reference. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `run_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): AutoAssignWebsitePhotosBody - `logo_full` · string (uuid) | null - `favicon` · string (uuid) | null **Responses** - `200` Successful Response: SuccessResponse_AutoAssignWebsitePhotosData_ - `request_id` · string · required - `success` · true - `data` · AutoAssignWebsitePhotosData · required - `assigned` · object - `skipped` · object - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Platform admin invites 1 endpoints. HTML: https://developers.keystone.app/api/internal/platform-admin-invites/ ### POST /api/v1/internal/platform-admin-invites/{invite_id}/consume Consume Platform Admin Invite Operation id: `consume_platform_admin_invite_api_v1_internal_platform_admin_invites__invite_id__consume_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `invite_id` | path | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): AccountInviteConsumeBody - `user_id` · string (uuid) · required - `email` · string (email) · required - `first_name` · string · required - `last_name` · string · required **Responses** - `200` Successful Response: SuccessResponse_PlatformAdminUserData_ - `request_id` · string · required - `success` · true - `data` · PlatformAdminUserData · required - `id` · string · required - `kind` · string · required - `first_name` · string - `last_name` · string - `email` · string (email) · required - `phone` · string | null - `role` · string · required - `status` · string · required - `delivery_status` · string | null - `joined_at` · string (date-time) · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Portal 1 endpoints. HTML: https://developers.keystone.app/api/internal/internal-portal/ ### POST /api/v1/internal/portal/lead_notify Portal Lead Notify Operation id: `portal_lead_notify_api_v1_internal_portal_lead_notify_post` Kairos callback: send one portal lead email with contact details as of now. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PortalLeadNotifyBody - `business_id` · string (uuid) · required - `contact_id` · string (uuid) · required - `lead_phone` · string **Responses** - `200` Successful Response: SuccessResponse_PortalLeadNotifyData_ - `request_id` · string · required - `success` · true - `data` · PortalLeadNotifyData · required - `status` · string · required - `reason` · string | null - `partial_details` · boolean | null - `has_name` · boolean | null - `has_email` · boolean | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Scheduler tick 1 endpoints. HTML: https://developers.keystone.app/api/internal/tick/ ### POST /api/v1/internal/tick Tick Operation id: `tick_api_v1_internal_tick_post` Process due auto-schedules. When ``BLOG_TICK_USE_WORKFLOW`` is set and control plane URL/token are configured, enqueues ``tpl-blog-generation-e2e`` per due schedule; otherwise generates posts in-process. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_TickResponse_ - `request_id` · string · required - `success` · true - `data` · TickResponse · required - `processed` · integer · required - `skipped` · integer - `errors` · integer · required - `details` · TickResultDetail[] - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Social posts 5 endpoints. HTML: https://developers.keystone.app/api/internal/social-internal/ ### POST /api/v1/internal/businesses/{business_id}/social_posts Create a social post draft (internal) Operation id: `create_social_post_api_v1_internal_businesses__business_id__social_posts_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `Idempotency-Key` | header | string \| null | no | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): SocialPostWriteBody - `content_markdown` · string · required - `link_url` · string | null - `photo_ids` · string[] - `target_profile_ids` · string[] - `target_formats` · SocialPostTargetFormatBody[] - `social_profile_id` · string · required - `format` · "facebook_feed" | "facebook_carousel" | "instagram_feed" | "instagram_carousel" · required - `publish_mode` · "draft" | "publish_now" | "schedule" - `scheduled_at` · integer | null - `timezone` · string | null - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null **Responses** - `201` Successful Response: SuccessResponse_SocialPostData_ - `request_id` · string · required - `success` · true - `data` · SocialPostData · required - `id` · string · required - `business_id` · string · required - `workflow_run_id` · string | null - `thread_id` · string | null - `content_markdown` · string · required - `link_url` · string | null - `photo_ids` · string[] - `media_status` · string | null - `status` · "draft" | "queued" | "publishing" | "published" | "failed" · required - `origin` · string - `scheduled_at` · integer | null - `timezone` · string | null - `published_at` · integer | null - `kairos_schedule_id` · string | null - `kairos_schedule_status` · string | null - `engagement` · SocialEngagementData - `last_engagement_synced_at` · integer | null - `media` · SocialPostPreviewMediaData[] - `targets` · SocialPostTargetData[] - `available_profiles` · SocialProfileData[] - `has_external_changes` · boolean - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/social_posts/{social_post_id} Get a social post (internal) Operation id: `get_social_post_api_v1_internal_businesses__business_id__social_posts__social_post_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `social_post_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_SocialPostData_ - `request_id` · string · required - `success` · true - `data` · SocialPostData · required - `id` · string · required - `business_id` · string · required - `workflow_run_id` · string | null - `thread_id` · string | null - `content_markdown` · string · required - `link_url` · string | null - `photo_ids` · string[] - `media_status` · string | null - `status` · "draft" | "queued" | "publishing" | "published" | "failed" · required - `origin` · string - `scheduled_at` · integer | null - `timezone` · string | null - `published_at` · integer | null - `kairos_schedule_id` · string | null - `kairos_schedule_status` · string | null - `engagement` · SocialEngagementData - `last_engagement_synced_at` · integer | null - `media` · SocialPostPreviewMediaData[] - `targets` · SocialPostTargetData[] - `available_profiles` · SocialProfileData[] - `has_external_changes` · boolean - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### PATCH /api/v1/internal/businesses/{business_id}/social_posts/{social_post_id} Update a social post draft (internal) Operation id: `update_social_post_api_v1_internal_businesses__business_id__social_posts__social_post_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `social_post_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): SocialPostWriteBody - `content_markdown` · string · required - `link_url` · string | null - `photo_ids` · string[] - `target_profile_ids` · string[] - `target_formats` · SocialPostTargetFormatBody[] - `social_profile_id` · string · required - `format` · "facebook_feed" | "facebook_carousel" | "instagram_feed" | "instagram_carousel" · required - `publish_mode` · "draft" | "publish_now" | "schedule" - `scheduled_at` · integer | null - `timezone` · string | null - `workflow_run_id` · string | null - `thread_id` · string | null - `media_status` · string | null **Responses** - `200` Successful Response: SuccessResponse_SocialPostData_ - `request_id` · string · required - `success` · true - `data` · SocialPostData · required - `id` · string · required - `business_id` · string · required - `workflow_run_id` · string | null - `thread_id` · string | null - `content_markdown` · string · required - `link_url` · string | null - `photo_ids` · string[] - `media_status` · string | null - `status` · "draft" | "queued" | "publishing" | "published" | "failed" · required - `origin` · string - `scheduled_at` · integer | null - `timezone` · string | null - `published_at` · integer | null - `kairos_schedule_id` · string | null - `kairos_schedule_status` · string | null - `engagement` · SocialEngagementData - `last_engagement_synced_at` · integer | null - `media` · SocialPostPreviewMediaData[] - `targets` · SocialPostTargetData[] - `available_profiles` · SocialProfileData[] - `has_external_changes` · boolean - `created_at` · integer · required - `updated_at` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/social_posts/{social_post_id}/publish Publish a social post immediately (internal) Operation id: `publish_social_post_api_v1_internal_businesses__business_id__social_posts__social_post_id__publish_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `social_post_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): SocialPostPublishBody - `target_profile_ids` · string[] - `target_formats` · SocialPostTargetFormatBody[] - `social_profile_id` · string · required - `format` · "facebook_feed" | "facebook_carousel" | "instagram_feed" | "instagram_carousel" · required **Responses** - `200` Successful Response: SuccessResponse_SocialPostPublishResult_ - `request_id` · string · required - `success` · true - `data` · SocialPostPublishResult · required - `social_post` · SocialPostData · required - `results` · SocialPostPublishTargetResult[] · required - `success` · boolean · required - `successful_count` · integer · required - `failed_count` · integer · required - `total_count` · integer · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/social_posts/generate Generate social post caption (internal) Operation id: `generate_social_post_api_v1_internal_businesses__business_id__social_posts_generate_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `Idempotency-Key` | header | string \| null | no | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): SocialPostGenerateBody - `prompt` · string | null - `target_profile_ids` · string[] - `platforms` · ("facebook" | "instagram")[] - `provider` · string | null **Responses** - `200` Successful Response: SuccessResponse_SocialPostGenerateData_ - `request_id` · string · required - `success` · true - `data` · SocialPostGenerateData · required - `content_markdown` · string · required - `media_briefs` · object[]: Photo briefs for the workflow worker to resolve against the media library, in display order. Empty unless social_media_briefs_enabled is on. The inline (non-workflow) callers return them but never act on them. - `metadata` · SocialPostGenerateMetadata · required - `generation_status` · string - `workflow_run_id` · string | null - `thread_id` · string | null - `social_post` · SocialPostData | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Social posts 1 endpoints. HTML: https://developers.keystone.app/api/internal/social-posts/ ### POST /api/v1/internal/social_posts/scheduled_publish Publish Scheduled Social Post Operation id: `publish_scheduled_social_post_api_v1_internal_social_posts_scheduled_publish_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): SocialPostScheduledPublishBody - `business_id` · string · required - `social_post_id` · string · required - `scheduled_at` · integer · required **Responses** - `200` Successful Response: SuccessResponse_SocialPostScheduledPublishData_ - `request_id` · string · required - `success` · true - `data` · SocialPostScheduledPublishData · required - `social_post_id` · string · required - `status` · "draft" | "queued" | "publishing" | "published" | "failed" | "skipped" · required - `skipped` · boolean - `reason` · string | null - `result` · SocialPostPublishResult | null - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Social workflow engine 3 endpoints. HTML: https://developers.keystone.app/api/internal/social-workflow-engine/ ### POST /api/v1/internal/workflow-engine/social/attach-photos Attach Social Photos Operation id: `attach_social_photos_api_v1_internal_workflow_engine_social_attach_photos_post` Attach photos to an existing draft without rewriting it. The photos are resolved after the draft is stored, and a PUT would demand the whole form and overwrite whatever it was not told. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolAttachSocialPhotosBody - `businessId` · string (uuid) · required - `socialPostId` · string (uuid) · required - `photoIds` · string (uuid)[] | null - `mediaStatus` · string | null - `workflowRunId` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/social/review-media Review Social Media Operation id: `review_social_media_api_v1_internal_workflow_engine_social_review_media_post` Vision-review the photo candidates the worker found in the library. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolReviewSocialMediaBody - `businessId` · string (uuid) · required - `mediaBrief` · object | null - `candidates` · object[] · required - `workflowRunId` · string | null - `ranked` · boolean **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/social/verify-photo Verify Social Photo Operation id: `verify_social_photo_api_v1_internal_workflow_engine_social_verify_photo_post` Does this reframed image work in a feed? One image, one verdict. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): ToolVerifySocialPhotoBody - `businessId` · string (uuid) · required - `mediaBrief` · object | null - `assetId` · string (uuid) · required - `intent` · object | null - `workflowRunId` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Website agent attachments 1 endpoints. HTML: https://developers.keystone.app/api/internal/website-agent-attachments/ ### GET /api/v1/internal/website-agent/attachments/{kind}/{file_id}/{name} Get Website Agent Attachment Operation id: `get_website_agent_attachment_api_v1_internal_website_agent_attachments__kind___file_id___name__get` One file of the token's request: a document's stored file, or a photo as WebP sized for the agent to look at. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `kind` | path | string | yes | | | `file_id` | path | string | yes | | | `name` | path | string | yes | | | `x-attachment-token` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Website generation 2 endpoints. HTML: https://developers.keystone.app/api/internal/website-gen/ ### POST /api/v1/internal/website-gen/callback Post Website Gen Callback Operation id: `post_website_gen_callback_api_v1_internal_website_gen_callback_post` Record the generator's outcome for a website. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | | `X-Keystone-Run-Token` | header | string \| null | no | | **Request body** (application/json, required): WebsiteGenCallbackRequest - `website_id` · string (uuid) · required - `gen_exit` · integer · required - `run_id` · string - `error` · string - `mode` · string - `revision` · integer | null - `checkpoint_sha` · string | null - `agent_reply` · string | null - `generator_version` · string - `prompt_version` · string - `model_used` · string **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/website-gen/github-token Post Website Gen Github Token Operation id: `post_website_gen_github_token_api_v1_internal_website_gen_github_token_post` A fresh installation token scoped to the run's one repository, while the run is live (409 ``RUN_NOT_LIVE`` otherwise). Replaces handing the run the GitHub App private key (``website_gen_token_vending_enabled``). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Keystone-Run-Token` | header | string \| null | no | | **Request body** (application/json, required): WebsiteGenGithubTokenRequest - `access` · "read" | "write" · required **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Website modification agent data 4 endpoints. HTML: https://developers.keystone.app/api/internal/website-mod-agent-data/ ### GET /api/v1/internal/website-mod/agent-data/{entity} List Agent Data Operation id: `list_agent_data_api_v1_internal_website_mod_agent_data__entity__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `entity` | path | string | yes | | | `X-Keystone-Data-Token` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_Any_ - `request_id` · string · required - `success` · true - `data` · any · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/website-mod/agent-data/{entity}/{target_id} Get Agent Data Record Operation id: `get_agent_data_record_api_v1_internal_website_mod_agent_data__entity___target_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `entity` | path | string | yes | | | `target_id` | path | string (uuid) | yes | | | `X-Keystone-Data-Token` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/website-mod/agent-data/schema Get Agent Data Schema Operation id: `get_agent_data_schema_api_v1_internal_website_mod_agent_data_schema_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `entity` | query | string | yes | | | `X-Keystone-Data-Token` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/website-mod/agent-data/write Post Agent Data Write Operation id: `post_agent_data_write_api_v1_internal_website_mod_agent_data_write_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Keystone-Data-Token` | header | string \| null | no | | **Request body** (application/json, required): AgentDataWriteBody - `entity` · string · required - `op` · "create" | "update" · required - `target_id` · string (uuid) | null - `fields` · object - `idempotency_key` · string · required - `summary` · string - `allow_duplicate` · boolean **Responses** - `200` Successful Response: SuccessResponse_AgentDataWriteData_ - `request_id` · string · required - `success` · true - `data` · AgentDataWriteData · required - `entity` · string · required - `op` · string · required - `target_id` · string · required - `label` · string · required - `replayed` · boolean - `field_changes` · AgentDataFieldChange[] - `record` · object - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Website modifications 17 endpoints. HTML: https://developers.keystone.app/api/internal/website-mod/ ### GET /api/v1/internal/businesses/{business_id}/website-mod/check-gate Get Website Mod Check Gate Operation id: `get_website_mod_check_gate_api_v1_internal_businesses__business_id__website_mod_check_gate_get` ``{blocked, status, problems, message}``: whether the pending change's browser check holds it back (the inbox router's guard on a chat "yes"). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/website-mod/context Get Website Mod Context Operation id: `get_website_mod_context_api_v1_internal_businesses__business_id__website_mod_context_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `verify_repo` | query | boolean | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/v1/internal/businesses/{business_id}/website-mod/pending-changes Get Pending Website Mod Changes Operation id: `get_pending_website_mod_changes_api_v1_internal_businesses__business_id__website_mod_pending_changes_get` List console-safe pending website-mod changes for a business (no GitHub PR fields). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/businesses/{business_id}/website-mod/requirements/texts Post Website Mod Requirement Texts Operation id: `post_website_mod_requirement_texts_api_v1_internal_businesses__business_id__website_mod_requirements_texts_post` Return decoded requirement document text for router clarify / plan hydration. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `business_id` | path | string (uuid) | yes | | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): RequirementTextsRequest - `file_ids` · string[] **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/website-mod/agent-callback Post Website Mod Agent Callback Operation id: `post_website_mod_agent_callback_api_v1_internal_website_mod_agent_callback_post` Store agent job outcome for SOR plan polling (Redis, keyed by plan_request_id). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): WebsiteModAgentCallbackRequest - `plan_request_id` · string · required - `website_id` · string | string (uuid) · required - `status` · string · required - `summary` · string - `agent_message` · string - `file_updates` · object[] - `primitives` · object[] - `error` · string - `cost_usd` · number | null - `duration_s` · number | null - `run_id` · string - `smoke` · object | null - `confirmation` · object | null - `denied_commands` · string[] - `stop_source` · string - `model` · string - `usage` · object | null - `browser_check` · object | null - `check_only` · boolean - `interrupt` · object | null - `notes_delivered` · string[] - `fetched_urls` · object[] - `notes_pending` · string[] **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/website-mod/agent-progress Post Website Mod Agent Progress Operation id: `post_website_mod_agent_progress_api_v1_internal_website_mod_agent_progress_post` Append a sanitized progress event for the live console activity feed. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): WebsiteModAgentProgressRequest - `plan_request_id` · string · required - `website_id` · string | string (uuid) · required - `seq` · integer · required - `ts` · integer | null - `kind` · string · required - `stage` · string | null - `text` · string | null - `tool` · string | null - `label` · string | null - `block` · string | null - `status` · string | null - `partial` · boolean - `artifact` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/website-mod/check-artifacts Post Website Mod Check Artifact Operation id: `post_website_mod_check_artifact_api_v1_internal_website_mod_check_artifacts_post` One browser-check screenshot from a session pod, while its run is going (or its "Check again" run is marked). The image is the raw request body. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `plan_request_id` | query | string | yes | | | `route` | query | string | yes | | | `viewport` | query | string | yes | | | `kind` | query | string | no | | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/website-mod/deploy-reconcile Post Website Mod Deploy Reconcile Operation id: `post_website_mod_deploy_reconcile_api_v1_internal_website_mod_deploy_reconcile_post` Kairos HTTP callback: poll pending website-mod deploys and self-reschedule. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-mod/cancel-pending Post Cancel Pending Website Mod Operation id: `post_cancel_pending_website_mod_api_v1_internal_workflow_engine_website_mod_cancel_pending_post` Clear Redis pending-change + release repo lock after reject/cancel. ``tombstone_run``: the engine, giving up on a plan, knows the run is over. The run's cancelled tombstone is laid FIRST — it is what stops a sor plan handler still executing past the engine's hangup from registering a change nobody can publish (it answers the cancelled terminal at its late check instead; prod KS-2485) — then the usual scoped cleanup runs. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): object **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-mod/deploy-status Post Deploy Status Operation id: `post_deploy_status_api_v1_internal_workflow_engine_website_mod_deploy_status_post` Poll GitHub Actions deploy.yml status for a pending website-mod production deploy. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): object **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-mod/gate-status Post Website Mod Gate Status Operation id: `post_website_mod_gate_status_api_v1_internal_workflow_engine_website_mod_gate_status_post` Read-mostly gate probe (Redis + open Keystone PRs + lock) after GH reconcile. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): object **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-mod/merge-deploy Post Merge Deploy Operation id: `post_merge_deploy_api_v1_internal_workflow_engine_website_mod_merge_deploy_post` Merge an approved PR and best-effort trigger ``deploy.yml``. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): object **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-mod/plan-and-open-pr Post Plan And Open Pr Operation id: `post_plan_and_open_pr_api_v1_internal_workflow_engine_website_mod_plan_and_open_pr_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PlanAndOpenPrRequest - `business_id` · string | string (uuid) · required - `workflow_run_id` · string - `thread_id` · string - `extracted_data` · object | null - `force_repo_map_refresh` · boolean - `plan_modes` · string[] | null **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-mod/preview-status Post Preview Status Operation id: `post_preview_status_api_v1_internal_workflow_engine_website_mod_preview_status_post` Poll for the Cloudflare version preview URL of a website-mod PR branch. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): object **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-mod/reconcile-pending Post Website Mod Reconcile Pending Operation id: `post_website_mod_reconcile_pending_api_v1_internal_workflow_engine_website_mod_reconcile_pending_post` Align Redis + repo_locks with open Keystone website-mod PRs (GitHub = SoT). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): object **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-mod/repo-lock/acquire Post Acquire Repo Lock Operation id: `post_acquire_repo_lock_api_v1_internal_workflow_engine_website_mod_repo_lock_acquire_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): object **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-mod/repo-lock/release Post Release Repo Lock Operation id: `post_release_repo_lock_api_v1_internal_workflow_engine_website_mod_repo_lock_release_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): object **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Website preview 1 endpoints. HTML: https://developers.keystone.app/api/internal/website-preview/ ### POST /api/v1/internal/website-previews/reap Reap website previews: fail stuck revisions, expire overdue drafts Operation id: `reap_website_previews_api_v1_internal_website_previews_reap_post` Sweep counts: ``revisions_failed`` (stuck past the reaper's clock), ``previews_expired`` (ready|failed past their +30d ``expires_at``, 0 while expiry is off), and the lost starts ``starts_resent``, ``revisions_never_started`` and ``builds_never_started``. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_WebsitePreviewReapResponse_ - `request_id` · string · required - `success` · true - `data` · WebsitePreviewReapResponse · required: Internal reap sweep counts (mark-only; V1 keeps resources). - `revisions_failed` · integer · required - `previews_expired` · integer · required - `starts_resent` · integer - `revisions_never_started` · integer - `builds_never_started` · integer - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Internal API: Website preview runs 7 endpoints. HTML: https://developers.keystone.app/api/internal/website-preview-runs/ ### POST /api/v1/internal/website-preview/agent-callback Post Website Preview Agent Callback Operation id: `post_website_preview_agent_callback_api_v1_internal_website_preview_agent_callback_post` Store an edit slot's result (2 h) for the workflow's settle. Only an edit run's token posts here: a build reports through the generator callback, and a stored result is settled as a slot's. A check-only run ("Check again") settles nothing: its check is recorded here. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | | `X-Keystone-Run-Token` | header | string \| null | no | | **Request body** (application/json, required): PreviewAgentCallbackRequest - `plan_request_id` · string · required - `website_id` · string | string (uuid) · required - `status` · string · required - `summary` · string - `agent_message` · string - `file_updates` · object[] - `primitives` · object[] - `error` · string - `cost_usd` · number | null - `duration_s` · number | null - `run_id` · string - `smoke` · object | null - `confirmation` · object | null - `denied_commands` · string[] - `stop_source` · string - `model` · string - `usage` · object | null - `browser_check` · object | null - `check_only` · boolean - `interrupt` · object | null - `notes_delivered` · string[] - `fetched_urls` · object[] - `notes_pending` · string[] - `checks` · object | null - `failure_kind` · string **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/website-preview/agent-progress Post Website Preview Agent Progress Operation id: `post_website_preview_agent_progress_api_v1_internal_website_preview_agent_progress_post` Append a sanitized event to the run's activity trail. A full build's ring is deeper than an edit's: it runs for most of an hour. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | | `X-Keystone-Run-Token` | header | string \| null | no | | **Request body** (application/json, required): WebsiteModAgentProgressRequest - `plan_request_id` · string · required - `website_id` · string | string (uuid) · required - `seq` · integer · required - `ts` · integer | null - `kind` · string · required - `stage` · string | null - `text` · string | null - `tool` · string | null - `label` · string | null - `block` · string | null - `status` · string | null - `partial` · boolean - `artifact` · string | null **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/website-preview/check-artifacts Post Website Preview Check Artifact Operation id: `post_website_preview_check_artifact_api_v1_internal_website_preview_check_artifacts_post` One browser-check screenshot from a preview edit's pod, under that run's token. The image is the raw request body. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `plan_request_id` | query | string | yes | | | `route` | query | string | yes | | | `viewport` | query | string | yes | | | `kind` | query | string | no | | | `x-internal-api-key` | header | string \| null | no | | | `X-Keystone-Run-Token` | header | string \| null | no | | **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-preview/runs/fail Post Preview Run Fail Operation id: `post_preview_run_fail_api_v1_internal_workflow_engine_website_preview_runs_fail_post` ``{state}`` — ``failed``, or the state a settled run already had. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PreviewRunFailBody - `preview_id` · string (uuid) · required - `revision` · integer - `kind` · "preview" | "edit" | "full" · required - `run_id` · string - `reason` · string **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-preview/runs/settle Post Preview Run Settle Operation id: `post_preview_run_settle_api_v1_internal_workflow_engine_website_preview_runs_settle_post` ``{state, checkpoint_sha, reason, fallback_eligible}``; idempotent. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PreviewRunRefBody - `preview_id` · string (uuid) · required - `revision` · integer - `kind` · "preview" | "edit" | "full" · required - `run_id` · string **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-preview/runs/start Post Preview Run Start Operation id: `post_preview_run_start_api_v1_internal_workflow_engine_website_preview_runs_start_post` ``{executor, run_id, deadline_s, state}``; idempotent per attempt. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PreviewRunStartBody - `preview_id` · string (uuid) · required - `revision` · integer - `kind` · "preview" | "edit" | "full" · required - `workflow_run_id` · string - `attempt` · integer - `force_executor` · "job" | "session" | "build_pod" | null **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/internal/workflow-engine/website-preview/runs/status Post Preview Run Status Operation id: `post_preview_run_status_api_v1_internal_workflow_engine_website_preview_runs_status_post` ``{state, executor, reason, fallback_eligible, deadline_s}``. Read-only but for one case: a later attempt whose launch was lost is failed here (no one else will), hence the commit. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `x-internal-api-key` | header | string \| null | no | | **Request body** (application/json, required): PreviewRunRefBody - `preview_id` · string (uuid) · required - `revision` · integer - `kind` · "preview" | "edit" | "full" · required - `run_id` · string **Responses** - `200` Successful Response: SuccessResponse_dict_str__Any__ - `request_id` · string · required - `success` · true - `data` · object · required - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse # Webhooks and callbacks Inbound calls from GitHub, Entri, Meta, and the voice provider. Endpoints third parties call into. Each verifies the provider's own signature header rather than a Keystone credential. - Audience: Third-party providers - Base URL: https://sor.keystone.app - Authentication: Provider signature. GitHub: `X-Hub-Signature-256`. Entri: `Entri-Signature-V3` with `Entri-Timestamp`. Meta and the voice provider verify their own signatures. - Endpoints: 7 in 4 groups - HTML: https://developers.keystone.app/api/webhooks/ ## Webhooks and callbacks: Ads platform webhooks 2 endpoints. HTML: https://developers.keystone.app/api/webhooks/webhook/ ### GET /api/v1/ads/webhook/{platform} Webhook subscription handshake (returns hub.challenge) Operation id: `webhook_verify_api_v1_ads_webhook__platform__get` Meta calls this once when the subscription is created — must echo back ``hub.challenge`` if our verify-token matches. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `platform` | path | string | yes | | | `hub.mode` | query | string | yes | | | `hub.challenge` | query | string | yes | | | `hub.verify_token` | query | string | yes | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/ads/webhook/{platform} Receive webhook event (signature-verified inline; ack 200 fast) Operation id: `webhook_receive_api_v1_ads_webhook__platform__post` Verify signature → dispatch by topic → ack. Verification uses the raw request body — read it before parsing to avoid HMAC mismatch from re-encoding. Failures return 401 silently; Meta retries automatically. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `platform` | path | string | yes | | | `X-Hub-Signature-256` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Webhooks and callbacks: Entri 1 endpoints. HTML: https://developers.keystone.app/api/webhooks/entri/ ### POST /api/v1/webhooks/entri Entri DNS-automation webhook (propagation status) Operation id: `entri_webhook_api_v1_webhooks_entri_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Entri-Signature-V3` | header | string \| null | no | | | `Entri-Timestamp` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Webhooks and callbacks: Github 1 endpoints. HTML: https://developers.keystone.app/api/webhooks/github/ ### POST /api/v1/webhooks/github Customer-site repo webhook (sitemap refresh + deploy completion) Operation id: `github_webhook_api_v1_webhooks_github_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Hub-Signature-256` | header | string \| null | no | | | `X-GitHub-Event` | header | string \| null | no | | **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## Webhooks and callbacks: Voice (Retell) 3 endpoints. HTML: https://developers.keystone.app/api/webhooks/retell/ ### POST /api/v1/voice/retell/events Retell Call Events Operation id: `retell_call_events_api_v1_voice_retell_events_post` Retell call-lifecycle webhook: every ended/analyzed call becomes a contact + call record (see ingest.py for the notification posture). ``call_ended`` writes the base record; ``call_analyzed`` upserts the summary and the post-call capture onto the same row (idempotent by provider call id). The capture (design doc P22) prefers the post-call analysis fields (``call_analysis.custom_analysis_data`` — whole-transcript extraction) and falls back to what the mid-call extract tool collected (``collected_dynamic_variables``); both are only present on ``call_analyzed``. Always 200 — Retell retries 5xxs and duplicates are wasted work. **Responses** - `200` Successful Response: object - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/voice/retell/inbound Retell Inbound Webhook Operation id: `retell_inbound_webhook_api_v1_voice_retell_inbound_post` Resolve dialed number → business, inject its context as dynamic variables. **Request body** (application/json, required): InboundWebhookRequest - `event` · string · required - `call_inbound` · InboundCallPayload | null - `from_number` · string | null - `to_number` · string | null - `agent_id` · string | null - `custom_sip_headers` · object | null **Responses** - `200` Successful Response: InboundWebhookResponse - `call_inbound` · InboundCallResponse: Fields Retell accepts back for a call (all optional). ``reject=True`` declines the call (S2: numbers Keystone doesn't know / has released); override_agent_id is not used — every number binds the shared agent. - `reject` · boolean | null - `dynamic_variables` · object | null - `metadata` · object | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### POST /api/v1/voice/retell/tool Retell Tool Webhook Operation id: `retell_tool_webhook_api_v1_voice_retell_tool_post` Retell custom-function endpoint: dispatch mid-call tool calls. Retell POSTs ``{"name": ..., "args": {...}, "call": {...}}`` signed with the same ``x-retell-signature`` scheme as the inbound webhook (same key). Tenant identity comes from ``call.metadata.business_id`` — the value OUR inbound webhook planted, which is why it's trustworthy (design doc §6.4). The response body is a plain JSON string: Retell hands it to the agent's LLM verbatim, so failures are honest instruction strings, never 5xx. **Responses** - `200` Successful Response: any - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse # System Health checks and site registration. Unauthenticated endpoints: liveness and readiness probes, and the sites registry lookup. - Audience: Keystone services - Base URL: https://sor.keystone.app - Authentication: No authentication - Endpoints: 4 in 3 groups - HTML: https://developers.keystone.app/api/system/ ## System: Health 2 endpoints. HTML: https://developers.keystone.app/api/system/api/ ### GET /api/health Health Operation id: `health_api_health_get` Service health check. Verifies DB connection before returning success. **Responses** - `200` Successful Response: HealthResponse - `request_id` · string · required - `success` · true - `data` · HealthData · required: Typed data for /api/health success response. - `status` · string - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ### GET /api/health/redis Health Redis Operation id: `health_redis_api_health_redis_get` Redis health check. Verifies Redis connection before returning success. **Responses** - `200` Successful Response: HealthResponse - `request_id` · string · required - `success` · true - `data` · HealthData · required: Typed data for /api/health success response. - `status` · string - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## System: Health 1 endpoints. HTML: https://developers.keystone.app/api/system/health/ ### GET /health Health Root Operation id: `health_root_health_get` Same behavior as ``GET /api/health``; path often used by load balancers and probes. **Responses** - `200` Successful Response: HealthResponse - `request_id` · string · required - `success` · true - `data` · HealthData · required: Typed data for /api/health success response. - `status` · string - `metadata` · ResponseMetadata | null - `pagination` · PaginationMetadata | null - `status_counts` · object | null - `lifecycle_counts` · LifecycleCounts | null - `classification_counts` · ClassificationCounts | null - `meta` · ResponseMeta | null - `total` · integer | null - `total_count` · integer | null - `page` · integer | null - `per_page` · integer | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse ## System: Sites by custom domain 1 endpoints. HTML: https://developers.keystone.app/api/system/by-custom-domain/ ### GET /api/v1/sites/by-custom-domain Get worker name by custom domain Operation id: `by_custom_domain_api_v1_sites_by_custom_domain_get` Public endpoint: map custom_domain to worker_name (website.repo_name) for routing. No auth. Returns 404 if not found. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `domain` | query | string | yes | Custom domain (hostname) to look up | **Responses** - `200` Successful Response: ByCustomDomainResponse - `worker_name` · string · required - `redirect_to` · string | null - `400` Bad request: ErrorResponse - `401` Unauthorized: ErrorResponse - `403` Forbidden: ErrorResponse - `404` Not found: ErrorResponse - `422` Validation error: ErrorResponse - `500` Internal server error: ErrorResponse - `503` Service unavailable: ErrorResponse # Auth service Sign-up, login, tokens, OAuth, users, roles, and permissions (Heimdal). Heimdal issues and verifies every token the other surfaces accept: console user tokens, consumer (website visitor) tokens, and MCP access tokens via OAuth. It also holds users, roles, and permissions. Operations tagged `internal` are service-to-service. - Audience: Console and first-party apps - Base URL: https://auth.keystone.app - Authentication: Varies by endpoint. Sign-in endpoints take credentials; everything else takes `Authorization: Bearer `. Endpoints tagged `internal` take `X-Internal-Api-Key`. - Endpoints: 45 in 9 groups - HTML: https://developers.keystone.app/api/auth/ ## Auth service: Auth 10 endpoints. HTML: https://developers.keystone.app/api/auth/auth/ ### POST /api/v1/auth/forgot-password Forgot Password Operation id: `forgot_password_api_v1_auth_forgot_password_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Recaptcha-Token` | header | string \| null | no | | | `X-Recaptcha-Key-Type` | header | string \| null | no | | **Request body** (application/json, required): ForgotPasswordRequest - `email` · string (email) · required **Responses** - `200` Successful Response: any - `422` Validation Error: HTTPValidationError ### POST /api/v1/auth/login Login Operation id: `login_api_v1_auth_login_post` OAuth2 compatible token login, get an access token for future requests. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Recaptcha-Token` | header | string \| null | no | | | `X-Recaptcha-Key-Type` | header | string \| null | no | | **Request body** (application/x-www-form-urlencoded, required): Body_login_api_v1_auth_login_post - `grant_type` · string | null - `username` · string · required - `password` · string (password) · required - `scope` · string - `client_id` · string | null - `client_secret` · string | null **Responses** - `200` Successful Response: Token - `access_token` · string · required - `token_type` · string · required - `refresh_token` · string · required - `422` Validation Error: HTTPValidationError ### POST /api/v1/auth/logout Logout Operation id: `logout_api_v1_auth_logout_post` **Request body** (application/json, required): LogoutRequest - `refresh_token` · string · required **Responses** - `200` Successful Response: any - `422` Validation Error: HTTPValidationError ### POST /api/v1/auth/refresh Refresh Token Operation id: `refresh_token_api_v1_auth_refresh_post` Rotate refresh token. **Request body** (application/json, required): RefreshTokenRequest - `refresh_token` · string · required **Responses** - `200` Successful Response: Token - `access_token` · string · required - `token_type` · string · required - `refresh_token` · string · required - `422` Validation Error: HTTPValidationError ### POST /api/v1/auth/resend-verification Resend Verification Operation id: `resend_verification_api_v1_auth_resend_verification_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Recaptcha-Token` | header | string \| null | no | | | `X-Recaptcha-Key-Type` | header | string \| null | no | | **Request body** (application/json, required): ResendVerificationRequest - `email` · string · required **Responses** - `200` Successful Response: any - `422` Validation Error: HTTPValidationError ### POST /api/v1/auth/reset-password Reset Password Operation id: `reset_password_api_v1_auth_reset_password_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Recaptcha-Token` | header | string \| null | no | | | `X-Recaptcha-Key-Type` | header | string \| null | no | | **Request body** (application/json, required): ResetPasswordRequest - `token` · string | null - `email` · string (email) | null - `otp` · string | null - `new_password` · string · required **Responses** - `200` Successful Response: any - `422` Validation Error: HTTPValidationError ### POST /api/v1/auth/signup Create User Operation id: `create_user_api_v1_auth_signup_post` Create new user (201), or resend the code to a pending one (200). **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Recaptcha-Token` | header | string \| null | no | | | `X-Recaptcha-Key-Type` | header | string \| null | no | | **Request body** (application/json, required): UserCreate - `email` · string (email) · required - `first_name` · string · required - `last_name` · string · required - `is_active` · boolean | null - `password` · string · required - `invite_id` · string | null **Responses** - `201` Successful Response: UserResponse - `email` · string (email) · required - `first_name` · string | null - `last_name` · string | null - `is_active` · boolean | null - `id` · string (uuid) · required - `role_id` · integer · required - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `avatar_url` · string | null - `422` Validation Error: HTTPValidationError ### POST /api/v1/auth/social Social Login Operation id: `social_login_api_v1_auth_social_post` Social Login (Google). Verifies ID Token, finds/creates user, issues JWTs. **Request body** (application/json, required): SocialLoginRequest - `provider` · string · required - `token` · string · required **Responses** - `200` Successful Response: Token - `access_token` · string · required - `token_type` · string · required - `refresh_token` · string · required - `422` Validation Error: HTTPValidationError ### GET /api/v1/auth/verify-email Verify Email Token Operation id: `verify_email_token_api_v1_auth_verify_email_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `token` | query | string | yes | | **Responses** - `200` Successful Response: any - `422` Validation Error: HTTPValidationError ### POST /api/v1/auth/verify-email Verify Email Otp Operation id: `verify_email_otp_api_v1_auth_verify_email_post` Verify by the emailed six-digit code and sign the user in. 409 ALREADY_VERIFIED for a verified account, 401 ACCOUNT_INACTIVE for an inactive one, 400 INVALID_OTP otherwise. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Recaptcha-Token` | header | string \| null | no | | | `X-Recaptcha-Key-Type` | header | string \| null | no | | **Request body** (application/json, required): VerifyEmailOtpRequest - `email` · string (email) · required - `otp` · string · required **Responses** - `200` Successful Response: Token - `access_token` · string · required - `token_type` · string · required - `refresh_token` · string · required - `422` Validation Error: HTTPValidationError ## Auth service: Consumer 4 endpoints. HTML: https://developers.keystone.app/api/auth/consumer/ ### POST /api/v1/consumer/auth/passwordless_auth Passwordless Auth Operation id: `passwordless_auth_api_v1_consumer_auth_passwordless_auth_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string | yes | | **Request body** (application/json, required): PasswordlessAuthBody - `phone` · string · required - `verification_token` · string · required - `first_name` · string · required - `last_name` · string · required - `email` · string · required - `webchat_session_id` · string | null **Responses** - `201` Successful Response: object - `422` Validation Error: HTTPValidationError ### POST /api/v1/consumer/auth/send_code Send Code Operation id: `send_code_api_v1_consumer_auth_send_code_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string | yes | | **Request body** (application/json, required): SendCodeBody - `phone` · string · required **Responses** - `200` Successful Response: object - `422` Validation Error: HTTPValidationError ### POST /api/v1/consumer/auth/verify_code Verify Code Operation id: `verify_code_api_v1_consumer_auth_verify_code_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string | yes | | **Request body** (application/json, required): VerifyCodeBody - `phone` · string · required - `code` · string · required **Responses** - `200` Successful Response: object - `422` Validation Error: HTTPValidationError ### GET /api/v1/consumer/me Consumer Me Operation id: `consumer_me_api_v1_consumer_me_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-API-Key` | header | string \| null | no | | | `authorization` | header | string \| null | no | | **Responses** - `200` Successful Response: object - `422` Validation Error: HTTPValidationError ## Auth service: Internal 9 endpoints. HTML: https://developers.keystone.app/api/auth/internal/ ### POST /api/v1/internal/account-invites/send-email Send Account Invite Email Operation id: `send_account_invite_email_api_v1_internal_account_invites_send_email_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, required): InternalAccountInviteEmailRequest - `email` · string (email) · required - `business_name` · string · required - `invite_id` · string · required - `owner_name` · string **Responses** - `200` Successful Response: any - `422` Validation Error: HTTPValidationError ### POST /api/v1/internal/billing-notifications/send-email Send Billing Notification Email Operation id: `send_billing_notification_email_api_v1_internal_billing_notifications_send_email_post` Email one business user about a billing event (DF-10). Called per recipient by sor, which owns the business↔user mapping; this service only knows how to render and send. **422 on a kind we have no copy for.** The alternative — generic wording — produces an email that names no amount, no date and no reason, which spends the customer's one notification on nothing and reports nothing to us. The caller retries and dead-letters it, so it surfaces as a tracked failure. `notification_data` gets no such treatment: it is best-effort, and a missing or unreadable value costs a line in the callout rather than the email. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, required): InternalBillingNotificationEmailRequest - `email` · string (email) · required - `business_name` · string · required - `kind` · string · required - `billing_url` · string (uri) | null - `notification_data` · object - `event_id` · string **Responses** - `200` Successful Response: any - `422` Validation Error: HTTPValidationError ### POST /api/v1/internal/escalation-notifications/send-email Send Escalation Notification Email Operation id: `send_escalation_notification_email_api_v1_internal_escalation_notifications_send_email_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, required): InternalEscalationNotificationEmailRequest - `email` · string (email) · required - `business_name` · string · required - `escalation_reason` · string - `consumer_label` · string - `matched_keyword` · string - `last_message_preview` · string - `conversation_url` · string (uri) | null **Responses** - `200` Successful Response: any - `422` Validation Error: HTTPValidationError ### POST /api/v1/internal/lead-notifications/send-email Send Lead Notification Email Operation id: `send_lead_notification_email_api_v1_internal_lead_notifications_send_email_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, required): InternalLeadNotificationEmailRequest - `email` · string (email) · required - `business_name` · string · required - `lead_name` · string - `lead_email` · string - `lead_phone` · string - `lead_message` · string - `contact_url` · string (uri) | null - `kind` · string - `source` · string - `lead_fields` · LeadFieldItem[] - `label` · string · required - `value` · string · required - `partial_details` · boolean - `ai_pursuing` · boolean **Responses** - `200` Successful Response: any - `422` Validation Error: HTTPValidationError ### POST /api/v1/internal/review-notifications/send-email Send Review Notification Email Operation id: `send_review_notification_email_api_v1_internal_review_notifications_send_email_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, required): InternalReviewNotificationEmailRequest - `email` · string (email) · required - `business_name` · string · required - `listing_title` · string - `reviewer_name` · string - `rating` · integer | null - `review_body` · string - `review_url` · string (uri) | null **Responses** - `200` Successful Response: any - `422` Validation Error: HTTPValidationError ### GET /api/v1/internal/users List Users Operation id: `list_users_api_v1_internal_users_get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `role_id` | query | integer | yes | Filter by Heimdal role_id | | `limit` | query | integer | no | | | `offset` | query | integer | no | | | `X-Internal-Api-Key` | header | string \| null | no | | **Responses** - `200` Successful Response: InternalUserListResponse - `users` · InternalUserRecord[] · required - `user_id` · string (uuid) · required - `email` · string (email) · required - `first_name` · string | null - `last_name` · string | null - `phone` · string | null - `role_id` · integer · required - `is_active` · boolean · required - `email_verified` · boolean · required - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `422` Validation Error: HTTPValidationError ### PATCH /api/v1/internal/users/{user_id} Update User Operation id: `update_user_api_v1_internal_users__user_id__patch` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `user_id` | path | string (uuid) | yes | | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, required): InternalUserUpdateRequest - `first_name` · string | null - `last_name` · string | null - `phone` · string | null - `email` · string (email) | null - `role_id` · integer | null - `is_active` · boolean | null **Responses** - `200` Successful Response: InternalUserRecord - `user_id` · string (uuid) · required - `email` · string (email) · required - `first_name` · string | null - `last_name` · string | null - `phone` · string | null - `role_id` · integer · required - `is_active` · boolean · required - `email_verified` · boolean · required - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `422` Validation Error: HTTPValidationError ### POST /api/v1/internal/users/lookup Lookup Users Operation id: `lookup_users_api_v1_internal_users_lookup_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, required): InternalUserLookupRequest - `user_ids` · string (uuid)[] - `emails` · string (email)[] **Responses** - `200` Successful Response: InternalUserLookupResponse - `users` · InternalUserRecord[] · required - `user_id` · string (uuid) · required - `email` · string (email) · required - `first_name` · string | null - `last_name` · string | null - `phone` · string | null - `role_id` · integer · required - `is_active` · boolean · required - `email_verified` · boolean · required - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `422` Validation Error: HTTPValidationError ### POST /api/v1/internal/users/provision Provision User Operation id: `provision_user_api_v1_internal_users_provision_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Internal-Api-Key` | header | string \| null | no | | **Request body** (application/json, required): InternalProvisionUserRequest - `email` · string (email) · required - `first_name` · string · required - `last_name` · string · required - `phone` · string | null - `role_id` · integer - `send_password_reset` · boolean **Responses** - `200` User updated successfully: InternalProvisionUserResponse - `user_id` · string (uuid) · required - `email` · string (email) · required - `first_name` · string | null - `last_name` · string | null - `phone` · string | null - `role_id` · integer · required - `created` · boolean · required - `password_reset_sent` · boolean · required - `201` Successful Response: InternalProvisionUserResponse - `user_id` · string (uuid) · required - `email` · string (email) · required - `first_name` · string | null - `last_name` · string | null - `phone` · string | null - `role_id` · integer · required - `created` · boolean · required - `password_reset_sent` · boolean · required - `422` Validation Error: HTTPValidationError ## Auth service: OAuth 3 endpoints. HTML: https://developers.keystone.app/api/auth/oauth/ ### GET /api/v1/oauth/requests/{request_id} Get Authorization Request Operation id: `get_authorization_request_api_v1_oauth_requests__request_id__get` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `request_id` | path | string (uuid) | yes | | **Responses** - `200` Successful Response: ConsentRequestInfo - `id` · string (uuid) · required - `client_name` · string · required - `client_host` · string · required - `scopes` · ScopeInfo[] · required - `name` · string · required - `description` · string · required - `resource` · string · required - `expires_at` · string (date-time) · required - `status` · string · required - `eligible` · boolean · required - `ineligible_reason` · string | null - `consented_before` · boolean - `422` Validation Error: HTTPValidationError ### POST /api/v1/oauth/requests/{request_id}/approve Approve Authorization Request Operation id: `approve_authorization_request_api_v1_oauth_requests__request_id__approve_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `request_id` | path | string (uuid) | yes | | **Responses** - `200` Successful Response: ConsentDecision - `redirect_to` · string · required - `422` Validation Error: HTTPValidationError ### POST /api/v1/oauth/requests/{request_id}/deny Deny Authorization Request Operation id: `deny_authorization_request_api_v1_oauth_requests__request_id__deny_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `request_id` | path | string (uuid) | yes | | **Responses** - `200` Successful Response: ConsentDecision - `redirect_to` · string · required - `422` Validation Error: HTTPValidationError ## Auth service: Permissions 5 endpoints. HTML: https://developers.keystone.app/api/auth/permissions/ ### GET /api/v1/permissions/ List Permissions Operation id: `list_permissions_api_v1_permissions__get` List all permissions. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `skip` | query | integer | no | | | `limit` | query | integer | no | | **Responses** - `200` Successful Response: PermissionResponse[] - `key` · string · required - `description` · string | null - `id` · integer · required - `422` Validation Error: HTTPValidationError ### POST /api/v1/permissions/ Create Permission Operation id: `create_permission_api_v1_permissions__post` Create new permission. Admin only. **Request body** (application/json, required): PermissionCreate - `key` · string · required - `description` · string | null **Responses** - `201` Successful Response: PermissionResponse - `key` · string · required - `description` · string | null - `id` · integer · required - `422` Validation Error: HTTPValidationError ### GET /api/v1/permissions/{permission_id} Get Permission Operation id: `get_permission_api_v1_permissions__permission_id__get` Get permission by ID. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `permission_id` | path | integer | yes | | **Responses** - `200` Successful Response: PermissionResponse - `key` · string · required - `description` · string | null - `id` · integer · required - `422` Validation Error: HTTPValidationError ### PUT /api/v1/permissions/{permission_id} Update Permission Operation id: `update_permission_api_v1_permissions__permission_id__put` Update permission. Admin only. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `permission_id` | path | integer | yes | | **Request body** (application/json, required): PermissionUpdate - `key` · string | null - `description` · string | null **Responses** - `200` Successful Response: PermissionResponse - `key` · string · required - `description` · string | null - `id` · integer · required - `422` Validation Error: HTTPValidationError ### DELETE /api/v1/permissions/{permission_id} Delete Permission Operation id: `delete_permission_api_v1_permissions__permission_id__delete` Delete permission. Admin only. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `permission_id` | path | integer | yes | | **Responses** - `204` Successful Response - `422` Validation Error: HTTPValidationError ## Auth service: Roles 7 endpoints. HTML: https://developers.keystone.app/api/auth/roles/ ### GET /api/v1/roles/ List Roles Operation id: `list_roles_api_v1_roles__get` List all roles. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `skip` | query | integer | no | | | `limit` | query | integer | no | | **Responses** - `200` Successful Response: RoleResponse[] - `name` · string · required - `description` · string | null - `id` · integer · required - `permissions` · PermissionResponse[] - `key` · string · required - `description` · string | null - `id` · integer · required - `422` Validation Error: HTTPValidationError ### POST /api/v1/roles/ Create Role Operation id: `create_role_api_v1_roles__post` Create new role. Admin only. **Request body** (application/json, required): RoleCreate - `name` · string · required - `description` · string | null - `id` · integer · required - `permission_ids` · integer[] | null **Responses** - `201` Successful Response: RoleResponse - `name` · string · required - `description` · string | null - `id` · integer · required - `permissions` · PermissionResponse[] - `key` · string · required - `description` · string | null - `id` · integer · required - `422` Validation Error: HTTPValidationError ### GET /api/v1/roles/{role_id} Get Role Operation id: `get_role_api_v1_roles__role_id__get` Get role by ID. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `role_id` | path | integer | yes | | **Responses** - `200` Successful Response: RoleResponse - `name` · string · required - `description` · string | null - `id` · integer · required - `permissions` · PermissionResponse[] - `key` · string · required - `description` · string | null - `id` · integer · required - `422` Validation Error: HTTPValidationError ### PUT /api/v1/roles/{role_id} Update Role Operation id: `update_role_api_v1_roles__role_id__put` Update role. Admin only. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `role_id` | path | integer | yes | | **Request body** (application/json, required): RoleUpdate - `name` · string | null - `description` · string | null **Responses** - `200` Successful Response: RoleResponse - `name` · string · required - `description` · string | null - `id` · integer · required - `permissions` · PermissionResponse[] - `key` · string · required - `description` · string | null - `id` · integer · required - `422` Validation Error: HTTPValidationError ### DELETE /api/v1/roles/{role_id} Delete Role Operation id: `delete_role_api_v1_roles__role_id__delete` Delete role. Admin only. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `role_id` | path | integer | yes | | **Responses** - `204` Successful Response - `422` Validation Error: HTTPValidationError ### POST /api/v1/roles/{role_id}/permissions Assign Permissions To Role Operation id: `assign_permissions_to_role_api_v1_roles__role_id__permissions_post` Assign permissions to a role. Admin only. Replaces all existing permissions with the provided list. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `role_id` | path | integer | yes | | **Request body** (application/json, required): RolePermissionAssign - `permission_ids` · integer[] · required **Responses** - `200` Successful Response: RoleResponse - `name` · string · required - `description` · string | null - `id` · integer · required - `permissions` · PermissionResponse[] - `key` · string · required - `description` · string | null - `id` · integer · required - `422` Validation Error: HTTPValidationError ### DELETE /api/v1/roles/{role_id}/permissions/{permission_id} Remove Permission From Role Operation id: `remove_permission_from_role_api_v1_roles__role_id__permissions__permission_id__delete` Remove a specific permission from a role. Admin only. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `role_id` | path | integer | yes | | | `permission_id` | path | integer | yes | | **Responses** - `200` Successful Response: RoleResponse - `name` · string · required - `description` · string | null - `id` · integer · required - `permissions` · PermissionResponse[] - `key` · string · required - `description` · string | null - `id` · integer · required - `422` Validation Error: HTTPValidationError ## Auth service: Root 1 endpoints. HTML: https://developers.keystone.app/api/auth/root/ ### GET / Root Operation id: `root__get` **Responses** - `200` Successful Response: any ## Auth service: Users 5 endpoints. HTML: https://developers.keystone.app/api/auth/users/ ### PATCH /api/v1/users/{user_id}/role Update User Role Operation id: `update_user_role_api_v1_users__user_id__role_patch` Update another user's role. Restricted to Admin User. **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `user_id` | path | string (uuid) | yes | | **Request body** (application/json, required): UserRoleUpdate - `role_id` · integer · required **Responses** - `200` Successful Response: UserResponse - `email` · string (email) · required - `first_name` · string | null - `last_name` · string | null - `is_active` · boolean | null - `id` · string (uuid) · required - `role_id` · integer · required - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `avatar_url` · string | null - `422` Validation Error: HTTPValidationError ### GET /api/v1/users/current_user Read Current User Operation id: `read_current_user_api_v1_users_current_user_get` Get current user. **Responses** - `200` Successful Response: UserResponse - `email` · string (email) · required - `first_name` · string | null - `last_name` · string | null - `is_active` · boolean | null - `id` · string (uuid) · required - `role_id` · integer · required - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `avatar_url` · string | null ### PUT /api/v1/users/current_user Update Current User Operation id: `update_current_user_api_v1_users_current_user_put` Update own profile. **Request body** (application/json, required): UserUpdate - `first_name` · string | null - `last_name` · string | null - `email` · string (email) | null - `password` · string | null - `old_password` · string | null **Responses** - `200` Successful Response: UserResponse - `email` · string (email) · required - `first_name` · string | null - `last_name` · string | null - `is_active` · boolean | null - `id` · string (uuid) · required - `role_id` · integer · required - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `avatar_url` · string | null - `422` Validation Error: HTTPValidationError ### POST /api/v1/users/current_user/avatar Upload Current User Avatar Operation id: `upload_current_user_avatar_api_v1_users_current_user_avatar_post` Upload own profile avatar (multipart/form-data). **Request body** (multipart/form-data, required): Body_upload_current_user_avatar_api_v1_users_current_user_avatar_post - `file` · string (binary) · required **Responses** - `200` Successful Response: UserResponse - `email` · string (email) · required - `first_name` · string | null - `last_name` · string | null - `is_active` · boolean | null - `id` · string (uuid) · required - `role_id` · integer · required - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `avatar_url` · string | null - `422` Validation Error: HTTPValidationError ### DELETE /api/v1/users/current_user/avatar Delete Current User Avatar Operation id: `delete_current_user_avatar_api_v1_users_current_user_avatar_delete` Remove own profile avatar. **Responses** - `200` Successful Response: UserResponse - `email` · string (email) · required - `first_name` · string | null - `last_name` · string | null - `is_active` · boolean | null - `id` · string (uuid) · required - `role_id` · integer · required - `created_at` · string (date-time) · required - `updated_at` · string (date-time) · required - `avatar_url` · string | null ## Auth service: Webhooks 1 endpoints. HTML: https://developers.keystone.app/api/auth/webhooks/ ### POST /api/v1/webhooks/sendgrid/events Sendgrid Events Operation id: `sendgrid_events_api_v1_webhooks_sendgrid_events_post` **Parameters** | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `X-Twilio-Email-Event-Webhook-Signature` | header | string \| null | no | | | `X-Twilio-Email-Event-Webhook-Timestamp` | header | string \| null | no | | **Responses** - `204` Successful Response - `422` Validation Error: HTTPValidationError # Keystone MCP server Endpoint: https://mcp.keystone.app/mcp. Streamable HTTP, stateless. Clients authenticate with an OAuth access token issued by the Auth service; the server forwards every call to the Edits API with that token and its own service key, and holds no data of its own. The model proposes changes; a user reviews each one on a card and applies it with a click the model cannot make. Source: https://github.com/Keystone-PZJR/keystone-mcp 9 tools. HTML: https://developers.keystone.app/mcp/ ## Tool: find_business Find a business. Called by the model, read-only. Find the business the user wants to work on, by name, website or domain. Shows a picker card: the user clicks the business, which opens an edit session, and its session id comes back to the model. The model cannot open a session or claim a business itself. A business can be edited while the user holds it (a claim, for 24 hours) and its site is not live; one nobody holds they can claim with a click on the card. **Input** | Field | Type | Required | Description | | --- | --- | --- | --- | | `query` | string (2–200) | yes | A business name, website or domain. | **Returns** Text listing each match as editable or the reason it is locked, and `structuredContent.view = "picker"` with the matches. `_meta.cardTokens` carries one token per click the card may make (open an editable business, claim an unclaimed one). **Edits API calls** - `GET /api/v1/edits/businesses` ## Tool: get_snapshot Business snapshot. Called by the model, read-only. Everything about the business in an edit session: the profile in full and every record (locations, services, service items, packages, team, FAQs, job postings, offers) with its id, label and version. Start here, before proposing changes. The user sees a card of what needs work. Hours live on the main location and are read separately so the card can show them. **Input** | Field | Type | Required | Description | | --- | --- | --- | --- | | `session_id` | uuid | yes | The edit session id the user opened (returned when they pick a business on the card). | **Returns** The snapshot as JSON text, and `structuredContent.view = "snapshot"` with the business, summary rows for the card, and a console URL. **Edits API calls** - `GET /api/v1/edits/sessions/{session_id}/snapshot` - `GET /api/v1/edits/sessions/{session_id}/records/{entity}/{record_id}` ## Tool: read_business Read a record. Called by the model, read-only. One record in full, with its write field names and version: the profile (entity "profile", record_id "profile") or any record get_snapshot lists. **Input** | Field | Type | Required | Description | | --- | --- | --- | --- | | `session_id` | uuid | yes | The edit session id the user opened (returned when they pick a business on the card). | | `entity` | string | yes | The entity, as get_snapshot names it. | | `record_id` | string | yes | The record id, or "profile" for the profile. | **Returns** The record as JSON text. **Edits API calls** - `GET /api/v1/edits/sessions/{session_id}/records/{entity}/{record_id}` ## Tool: propose_changes Propose changes. Called by the model. Stage changes to the business for the user to review. Nothing is saved: they see each change on a card that names the business, untick any they do not want, and click Apply. Send every change in one call, up to 50. Include `expected` (the values read) or `version`, so anything changed since is caught. Changes that look like they came from another of the user's businesses start unticked. The call is idempotent on its content within a session. **Input** | Field | Type | Required | Description | | --- | --- | --- | --- | | `session_id` | uuid | yes | The edit session id the user opened (returned when they pick a business on the card). | | `summary` | string (≤500) | no | One line saying what these changes do. | | `items` | ChangeItem[] (1–50) | yes | The changes; see ChangeItem below. | **Returns** Text naming the staged changeset and each change, with a console link for applying without the card; `structuredContent.view = "changeset"`; `_meta.cardTokens.apply` for the card's Apply click. Changes SOR refuses are listed by position with a reason so the model can fix only those. **Edits API calls** - `POST /api/v1/edits/sessions/{session_id}/changesets` ## Tool: discard_changeset Discard a draft. Called by the model. Drop a staged changeset the user no longer wants. Nothing was saved from it. **Input** | Field | Type | Required | Description | | --- | --- | --- | --- | | `changeset_id` | uuid | yes | The changeset id propose_changes returned. | **Returns** Confirmation text. **Edits API calls** - `POST /api/v1/edits/changesets/{changeset_id}/discard` ## Tool: claim_business Claim this business. Called by the card. Called by the Keystone card when the user clicks Claim on the business picker. Claims an unclaimed, unlaunched business for the caller for 24 hours so nobody else can take it meanwhile. **Input** | Field | Type | Required | Description | | --- | --- | --- | --- | | `business_id` | uuid | yes | The business id, as find_business lists it. | | `card_token` | string | yes | The token the card was given for this exact action and target. Minted per click; the model never has one. | **Returns** Text with the claim's expiry; `structuredContent.view = "claimed"`; a fresh `open:` card token for the next click. **Edits API calls** - `PUT /api/v1/edits/businesses/{business_id}/claim` ## Tool: open_session Work on this business. Called by the card. Called by the Keystone card when the user picks a business. Binds the chat to that business by opening an edit session; the model then uses the session id for get_snapshot and propose_changes. **Input** | Field | Type | Required | Description | | --- | --- | --- | --- | | `business_id` | uuid | yes | The business id, as find_business lists it. | | `card_token` | string | yes | The token the card was given for this exact action and target. Minted per click; the model never has one. | **Returns** Text naming the business, the session id and its expiry; `structuredContent.view = "session"`. **Edits API calls** - `POST /api/v1/edits/sessions` ## Tool: apply_changeset Apply changes. Called by the card, destructive. Called by the Keystone card when the user clicks Apply. Applies the ticked items (or exactly the ones given). Each applied change lands in the console's Change History. **Input** | Field | Type | Required | Description | | --- | --- | --- | --- | | `changeset_id` | uuid | yes | The changeset id propose_changes returned. | | `item_ids` | uuid[] (≤50) | no | Exactly these items; omitted, the ticked ones. | | `card_token` | string | yes | The token the card was given for this exact action and target. Minted per click; the model never has one. | **Returns** Text with a tally (applied, skipped, refused) and each change; `structuredContent.view = "changeset"`; an `undo` card token. **Edits API calls** - `POST /api/v1/edits/changesets/{changeset_id}/apply` ## Tool: undo_changeset Undo changes. Called by the card, destructive. Called by the Keystone card when the user clicks Undo. Reverts an applied changeset. A value someone changed again since the apply is kept, so an undo can undo nothing; the text says so. **Input** | Field | Type | Required | Description | | --- | --- | --- | --- | | `changeset_id` | uuid | yes | The changeset id propose_changes returned. | | `card_token` | string | yes | The token the card was given for this exact action and target. Minted per click; the model never has one. | **Returns** Text with what was reverted and what was kept; `structuredContent.view = "changeset"`. **Edits API calls** - `POST /api/v1/edits/changesets/{changeset_id}/revert` ## ChangeItem One change inside `propose_changes`. What the model may send is exactly what the Edits API accepts. | Field | Type | Required | Description | | --- | --- | --- | --- | | `entity` | string | yes | The entity, as get_snapshot names it: profile, location, service, service_item, package, team_member, faq, job_posting, offer. | | `op` | "create" \| "update" \| "delete" | yes | What to do. | | `record_id` | string | no | The record to update or delete; omit for the profile and for creates. | | `fields` | object | no | New values by write field name; a nested object may be sent in part. | | `expected` | object | no | The values you read for those fields, so a change made since is caught (compare-and-swap). | | `version` | string | no | The record version you read, from get_snapshot or read_business. An alternative to expected. |