APIs and execution contracts
The authenticated quote seam, execution lifecycle, provider adapters and complete source route inventory.
Authenticated no-spend quote
Observed source: hosted-app/app/api/query-manifest/route.js. POST authenticates first, requires run_pulls scope, uses a process-local 30-request/60-minute owner bucket and bounds JSON to 131072 bytes. API-key authentication attempts a last_used_at write: no-spend does not mean no-write.
With truthy pull_plan, the route compiles an explicit selection using server-controlled price evidence. Responses include ok, issues, manifest and spend_enabled; plan can be absent on early validation failure. A ready quote is not approval to dispatch and spend_enabled is a runtime switch, not consent.
Anonymous requests return 401/signup_required, wrong scope 403/insufficient_scope, rate limiting 429/too_many_manifests. JSON read failures use the bounded reader's status. Without pull_plan the same route uses the legacy manifest branch; it must be documented and tested separately.
{
"domain": "example.com",
"seo_market": {
"location_code": 2840,
"language_code": "en",
"confirmed": true
},
"pull_plan": {
"preset": "low",
"items": {
"own_rankings": {
"included": true,
"groups": [
{
"id": "g1",
"label": "Domain / locale query 1",
"included": true,
"active_zero": false,
"values": {
"rows": 500
}
}
],
"options": {
"clickstream": "off"
}
}
}
},
"plan_bindings": {}
}Synthetic request example: one current-catalog own_rankings group, 500 rows, US/English. All omitted items are OFF. This is not an Alaris quote, current external price verification or purchase approval.
Selected-plan request fields
| Field or condition | Observed contract | Boundary |
|---|---|---|
| Authorization: Bearer | Supabase session token or Brand Studio API key with run_pulls scope. | Authenticate before scope, rate bucket and body reading. Credentials never belong in JSON. |
| domain / url | Truthy domain takes precedence over url; the compiler normalizes an HTTP(S) hostname. | Rejects credentials, explicit ports, IP/localhost and private-style suffixes. Syntax validation is not live public-DNS proof. |
| pull_plan | Explicit selection object; truthy value selects the selected-plan branch. | Missing/falsy value uses the legacy branch. Omitted catalog rows mean OFF, not preset defaults. |
| seo_market | confirmed=true, positive-integer location_code, lowercase language_code such as en or en-us. | Required by the compiler; US/English fixtures use 2840/en. This is separate from the business address. |
| plan_bindings | Object, defaults to {} when omitted; null/arrays refused. | social_profiles must be an array if present. Active items require their specific confirmed bindings. |
| tier / pull_profile_id | Resolved by pullTierForRequest; determines separate customer-credit pricing. | Selected quote uses pull_profile_id or PULL_DEPTH_DEFAULT; legacy also considers depth. Preset and billing tier are not interchangeable. |
| user_id / client price fields | Not ownership, pricing or authorization authority for this route. | Uses auth.user.id and server-provided price evidence. Quoting does not grant purchase permission. |
| plan_hash / approved_max_cost_usd | Not checked as approval by the quote route. | The later reservation route recompiles and requires the exact current hash and maximum. |
Selection and group structure
| Field or condition | Observed contract | Boundary |
|---|---|---|
| pull_plan.items | Catalog-ID keyed rows, at most 111 supplied keys; unknown IDs refused. | Current gap: {} and [] both reach ready with no supported selections. |
| items[id].included / groups | included must be boolean; groups must be an array. | Group count is bounded by the item's catalog max_groups, otherwise 20. |
| groups[].id / values | Truthy unique ID; values is a non-array object of integer quantities. | Quantities are 0..100000, then catalog and actual adapter ceilings also apply. Labels are not target bindings. |
| groups[].included / active_zero | included=false omits a group; active_zero requests a zero-result probe. | Executable alpha refuses active_zero; zero quantity never silently buys a minimum request. |
| items[id].options | Catalog-defined options; selected groups/options must survive normalization unchanged. | Unsupported changes are refused, not silently clamped. Clickstream stays off. |
Confirmed target bindings
| Field or condition | Observed contract | Boundary |
|---|---|---|
| rival_rankings[group_id] / keyword_gaps[group_id] | Different public domain, confirmed=true, nonblank reason; optional source_url. | No duplicate rival domain within a collection; search overlap alone is not business-competitor confirmation. |
| reviews[group_id] | Confirmed same-domain website plus supported place_id/cid or public Maps URL parameters; optional name/source_url. | No short-link expansion or guessed identity. Conflicting IDs and duplicate normalized profiles refused; CID strings preserve large integers. |
| website_urls | Exactly the selected fc_pages count; unique absolute same-domain non-PDF URLs. | No hidden discovery; requires separate server-verified Firecrawl credit-cost evidence. |
| social_profiles[] | Exactly one confirmed {platform,url} for each selected platform. | Allowed platform hostname and nonempty profile path; labels are not account bindings. |
| lighthouse[group_id] | Owned target/subdomain URL plus device=desktop|mobile; one test per group. | Unique page/device pairs; site-evidence combined ceiling is 30 tasks/groups. |
| ads.facebook | confirmed=true, numeric page_id of 5..30 digits, Facebook source_url. | One bound list request; no implicit pagination or detail purchases. |
Selected quote response fields
| Field or condition | Observed contract | Boundary |
|---|---|---|
| status / ok / issues | ready/true or needs_review/false; issues[] contains item_id, reason and optional detail. | Compiler review state, not authorization. Early auth/body refusals can omit ok/issues. |
| plan | Compiled source-bound execution plan; some later refusals can include it. | Early validation failures omit plan entirely; do not assume every HTTP 422 has one. |
| plan.schema_version / rate_version | brand-studio-execution/v1 and the versioned local rate basis. | Version labels are not proof that current external provider rates were refreshed. |
| plan.execution_contract | adapter_version, source_sha256, sorted capability_ids. | Binds producer dependency sources; source changes invalidate the prior approval context. |
| plan.domain / market / preset / selection | Canonical target, confirmed locale, preset and normalized explicit selection. | Do not reconstruct a purchase from a preset label or omitted rows. |
| plan.rows[] | item_id, status, groups, max_cost_usd; leg/rate_id/source_ids when applicable. | Supported, parked, unsupported, conditional-not-implemented and not-selected are different. None is an execution receipt. |
| plan.legs / plan_hash | seo, backlinks, reviews, website, social, site_evidence and ads configurations; each carries domain, market, max_cost_usd, plan_id. | Every leg plan_id equals plan_hash. Rival/gap and site-evidence groups retain individual targets and quantities. |
| plan.max_cost_usd / price_kind | Provider maximum in USD; maximum_provider_cost_not_guaranteed_charge. | Not actual charge, account movement or full report cost. |
| plan.customer_credit_price | credits, basis, tier, byo_discount_resolved_at_reservation. | Existing hosted-tier price, separate from provider dollars; final billing rules still require reservation/ledger proof. |
| manifest | query-manifest-v1 view; null on compile failure. | Successful projection uses reserved_execution_plan / approved_scope labels even before reservation. These labels do not prove approval or stored execution. |
| spend_enabled | Exact switch BRAND_STUDIO_PLAN_RUNS_ENABLED==='true'. | True does not prove human approval, server cap, sufficient credits, built runtime or operator approval ID. |
Limits, errors and side effects
| Field or condition | Observed contract | Boundary |
|---|---|---|
| Authentication/scope | 401 refused/signup_required + detail; 403 refused/insufficient_scope + required_scope. | Real route/auth modules exercised with synthetic transport, not a live account. |
| Rate limiting | 429 rate_limited/too_many_manifests after 30 allowed attempts in a 60-minute owner bucket. | Process-local fixed window from first use, not wall-clock-hour or distributed quota. Restart resets it; above 5000 owners the Map clears. |
| Body size/JSON | 131072 bytes; 413 body_too_large; 400 invalid_json. | Streamed and advertised length checked. Authentication and allowed-request quota consumption occur first. |
| Read timeouts | Body read 10 seconds; 504 request_deadline, 499 request_aborted, 502 readable_body_required. | Source-defined, not measured in this probe. Route maxDuration=30 is deployment configuration, not a measured latency promise. |
| Compilation refusal | 422 needs_review with issues and manifest=null; plan may be omitted. | Includes invalid_domain, market_confirmation_required, invalid_bindings, explicit_plan_required, invalid_selection, invalid_quantity, unsupported_selection, parked_module and item-specific reasons. |
| API-key auth side effect | Hashed-key lookup then best-effort PATCH of last_used_at. | Happens before scope/quota/body checks. No-provider-spend is NOT no-database-write. |
| Session authentication | GET /auth/v1/user; 15-second / 1-MiB bound. | Does not update API-key usage. Fixture observations made no live auth request. |
| Execution is a later gate | Queue mode, exact current hash/maximum, built runtime, enabled server cap and exact operator plan/user/approval ID. | No settings changed. The previous funded attempt is not reusable permission. |
Observed gaps and verification status
On 2026-09-13 the coordinator exercised the actual Next route, auth resolver and compiler with synthetic auth transport only. Eight observations record response keys, issues, selected items and auth side effects; external fetches are refused. This is module-level runtime evidence, not a listening HTTP server, live identity or independent funded acceptance.
Empty items={} and items=[] both returned HTTP 200/ready with no selected capabilities, maximum provider cost 0 and fixture tier price 1 credit. This is a readiness/schema gap, not proof that a charge occurred. Require nonempty executable scope and an object-shaped items record before acceptance.
A synthetic Firecrawl price-basis timestamp of 2000-01-01 was accepted when unit cost/source were otherwise valid. The source checks parseability but no freshness window. Lead review must establish expiry policy and stale-source refusal; this pass does not invent a threshold or activate the provider.
Existing route/compiler/review tests pass but do not cover empty-scope refusal or age-expiry requirements. Implementation and new independent tests remain assigned to the Claude engineers, whose retry gate is unresolved.
Quote source and reproduction
Sources: hosted-app/app/api/query-manifest/route.js; hosted-app/lib/pull-execution-plan.mjs; hosted-app/lib/comprehensive-pull.mjs (resolveAuthContext/resolveApiKeyContext); hosted-app/lib/bounded-http.mjs; hosted-app/lib/comprehensive-pull-handler.mjs (reservation approval gates); hosted-app/lib/plan-review-response.mjs.
Run node docs/plans/brand-studio-roadmap/quote-contract-observation-20260913.mjs for the no-external-network fixture observation. Its paired .json records observations, probe hash and execution source hash. Synthetic prices and identities must never be copied into a live quote or approval.
Existing checks: node --test tests/query-manifest-route.test.mjs tests/pull-execution-plan.test.mjs tests/plan-review-response.test.mjs. The remaining route inventory still needs field-level coverage; this reference does not certify every API.
Core public seams
| Seam | Responsibility | Required contract proof |
|---|---|---|
| POST /api/comprehensive | Validate current approval and reserve a selected plan for execution. | Reject changed digest, wrong owner, unresolved targets and insufficient credits before purchase. |
| GET /api/comprehensive; POST /api/comprehensive/advance | Observe or advance the stored run through existing queue contracts. | Document polling side effects; prove fencing, process restart and closed-tab continuation. |
| GET /api/credits | Return authenticated complete balance and ledger state. | Missing RPC, malformed snapshot or partial read cannot look like available credit. |
| GET /api/pulls; /pull/[pull_id] | Find and reload the same saved result. | Ownership, terminal status and retained evidence survive browser and process reload. |
| GET /api/brand-studio/brand-kit; /brand-presence | Export from the selected saved capture. | Export, visible claims and receipt agree; private fields and signed credentials do not leak. |
| POST /api/brand-studio/retry-leg | Legacy retry seam with selected-plan protection. | Existing focused tests refuse selected-plan retries before write or collection; broader runtime proof remains required. |
| POST /api/mcp | Programmatic workflow via scoped tools. | Each tool maps to the same authority, versions, approval and cost gates as the HTTP path. |
Every endpoint needs a contract
Complete the route inventory with request/response schemas, auth and ownership scope, pagination and ordering, error status/reason codes, input size limits, timeouts, rate limits, idempotency, side effects and version compatibility.
Include webhook/cron, download, dynamic path and non-/api handlers where relevant. The generated inventory below discovers local /api route declarations; it does not prove deployed availability or exhaustive middleware behavior.
For each provider adapter, document exact endpoint/version, request quantities and units, locale, target identity, task ownership, paging/continuation, retry/cancel policy, response schema, rate basis, actual-cost extraction and normalization. Link official provider documentation through the existing provider pages and refresh drift-prone details before use.
Supported, parked and legacy behavior
Catalog support, adapter implementation, configuration and observed successful execution are four different states. Show all four. A free item with no working adapter is not complete. A configured key does not authorize new work.
The request ledger refuses new spending when an earlier request is reserved or uncertain. A free poll must belong to the same plan, provider task and target. Make these properties explicit at every adapter and continuation seam.
Process-local daily provider ceilings are not shared account-wide limits and can reset on process restart. Distributed enforcement and concurrent-account reconciliation require separate evidence.
AI visibility and unpriced lanes stay parked. Existing legacy stages may still be present in queue ordering; selected-plan progress must expose them truthfully as skipped instead of implying active purchased work.
Route declarations from current source
This inventory is regenerated from route files. A declared method does not prove authentication, schema coverage, runtime success or support status. Contract coverage must be checked separately.
| Method | Route | Source |
|---|---|---|
| DELETE, GET, POST | /api/account/api-keys | hosted-app/app/api/account/api-keys/route.js |
| DELETE, GET | /api/account/mcp-connections | hosted-app/app/api/account/mcp-connections/route.js |
| GET, POST | /api/admin | hosted-app/app/api/admin/route.js |
| POST | /api/admin/topup | hosted-app/app/api/admin/topup/route.js |
| POST | /api/auth/google/exchange | hosted-app/app/api/auth/google/exchange/route.js |
| GET | /api/auth/google | hosted-app/app/api/auth/google/route.js |
| POST | /api/auth | hosted-app/app/api/auth/route.js |
| POST | /api/brand-check | hosted-app/app/api/brand-check/route.js |
| POST | /api/brand-refresh | hosted-app/app/api/brand-refresh/route.js |
| POST | /api/brand-studio/ai-link | hosted-app/app/api/brand-studio/ai-link/route.js |
| GET | /api/brand-studio/brand-delta | hosted-app/app/api/brand-studio/brand-delta/route.js |
| GET | /api/brand-studio/brand-kit | hosted-app/app/api/brand-studio/brand-kit/route.js |
| GET | /api/brand-studio/brand-presence | hosted-app/app/api/brand-studio/brand-presence/route.js |
| GET | /api/brand-studio/proxy-asset | hosted-app/app/api/brand-studio/proxy-asset/route.js |
| POST | /api/brand-studio/retry-leg | hosted-app/app/api/brand-studio/retry-leg/route.js |
| POST | /api/brand-studio/run-override | hosted-app/app/api/brand-studio/run-override/route.js |
| POST | /api/brand-studio/visual-direction | hosted-app/app/api/brand-studio/visual-direction/route.js |
| GET | /api/brands/basic/changes | hosted-app/app/api/brands/basic/changes/route.js |
| DELETE, GET, POST | /api/brands/basic | hosted-app/app/api/brands/basic/route.js |
| GET | /api/brands/change-evidence | hosted-app/app/api/brands/change-evidence/route.js |
| GET | /api/brands/changes | hosted-app/app/api/brands/changes/route.js |
| GET | /api/brands/export | hosted-app/app/api/brands/export/route.js |
| GET | /api/brands/review-changes | hosted-app/app/api/brands/review-changes/route.js |
| GET, POST | /api/brands/review-watch | hosted-app/app/api/brands/review-watch/route.js |
| GET, POST | /api/brands/review-watch/sweep | hosted-app/app/api/brands/review-watch/sweep/route.js |
| POST | /api/brands/reviews/snapshot | hosted-app/app/api/brands/reviews/snapshot/route.js |
| GET | /api/brands | hosted-app/app/api/brands/route.js |
| GET | /api/brands/search-changes | hosted-app/app/api/brands/search-changes/route.js |
| GET, POST | /api/brands/search-watch | hosted-app/app/api/brands/search-watch/route.js |
| GET, POST | /api/brands/search-watch/sweep | hosted-app/app/api/brands/search-watch/sweep/route.js |
| GET, POST | /api/brands/site-pages | hosted-app/app/api/brands/site-pages/route.js |
| GET | /api/brands/social-changes | hosted-app/app/api/brands/social-changes/route.js |
| GET, POST | /api/brands/social-watch | hosted-app/app/api/brands/social-watch/route.js |
| GET, POST | /api/brands/social-watch/sweep | hosted-app/app/api/brands/social-watch/sweep/route.js |
| GET, POST | /api/brands/watch | hosted-app/app/api/brands/watch/route.js |
| GET, POST | /api/brands/watch/sweep | hosted-app/app/api/brands/watch/sweep/route.js |
| DELETE, GET, POST | /api/byo-keys | hosted-app/app/api/byo-keys/route.js |
| POST | /api/client-review/ai-fix | hosted-app/app/api/client-review/ai-fix/route.js |
| GET, POST | /api/client-review/overrides | hosted-app/app/api/client-review/overrides/route.js |
| POST | /api/client-review/requests/[id] | hosted-app/app/api/client-review/requests/[id]/route.js |
| GET | /api/client-review/requests | hosted-app/app/api/client-review/requests/route.js |
| POST | /api/client-review/rounds | hosted-app/app/api/client-review/rounds/route.js |
| POST | /api/client-review/shares/[id]/revoke | hosted-app/app/api/client-review/shares/[id]/revoke/route.js |
| GET, POST | /api/client-review/shares | hosted-app/app/api/client-review/shares/route.js |
| GET, POST | /api/comprehensive/advance | hosted-app/app/api/comprehensive/advance/route.js |
| GET | /api/comprehensive/preview | hosted-app/app/api/comprehensive/preview/route.js |
| GET, POST | /api/comprehensive | hosted-app/app/api/comprehensive/route.js |
| POST | /api/comprehensive/sweep | hosted-app/app/api/comprehensive/sweep/route.js |
| GET, POST | /api/credits/checkout | hosted-app/app/api/credits/checkout/route.js |
| GET | /api/credits | hosted-app/app/api/credits/route.js |
| POST | /api/credits/topup | hosted-app/app/api/credits/topup/route.js |
| POST | /api/credits/webhook | hosted-app/app/api/credits/webhook/route.js |
| POST | /api/docs-ask | hosted-app/app/api/docs-ask/route.js |
| GET | /api/instant | hosted-app/app/api/instant/route.js |
| POST | /api/intake-peek | hosted-app/app/api/intake-peek/route.js |
| POST | /api/interview-prefill | hosted-app/app/api/interview-prefill/route.js |
| GET | /api/local-runs/[run_id] | hosted-app/app/api/local-runs/[run_id]/route.js |
| GET | /api/local-runs | hosted-app/app/api/local-runs/route.js |
| GET, POST | /api/mcp/oauth/authorize | hosted-app/app/api/mcp/oauth/authorize/route.js |
| POST | /api/mcp/oauth/revoke | hosted-app/app/api/mcp/oauth/revoke/route.js |
| POST | /api/mcp/oauth/token | hosted-app/app/api/mcp/oauth/token/route.js |
| DELETE, GET, POST | /api/mcp | hosted-app/app/api/mcp/route.js |
| POST | /api/notifications/dispatch | hosted-app/app/api/notifications/dispatch/route.js |
| GET, POST | /api/notifications/outbox | hosted-app/app/api/notifications/outbox/route.js |
| GET, POST | /api/notifications/preferences | hosted-app/app/api/notifications/preferences/route.js |
| GET, POST | /api/notifications/sweep | hosted-app/app/api/notifications/sweep/route.js |
| GET, POST | /api/notifications/unsubscribe | hosted-app/app/api/notifications/unsubscribe/route.js |
| POST | /api/pulls/complete-section | hosted-app/app/api/pulls/complete-section/route.js |
| GET | /api/pulls | hosted-app/app/api/pulls/route.js |
| POST | /api/pulls/section-review-plan | hosted-app/app/api/pulls/section-review-plan/route.js |
| GET, POST | /api/pulls/section-review-retry | hosted-app/app/api/pulls/section-review-retry/route.js |
| POST | /api/query-manifest | hosted-app/app/api/query-manifest/route.js |
| GET | /api/review/[token]/media | hosted-app/app/api/review/[token]/media/route.js |
| POST | /api/review/[token]/requests | hosted-app/app/api/review/[token]/requests/route.js |
| GET | /api/review/[token] | hosted-app/app/api/review/[token]/route.js |
| GET | /api/search | hosted-app/app/api/search/route.ts |