openapi: 3.0.3
info:
  title: Colter Public API
  description: |
    Paste a Shopify product link and Colter tells you how to improve the page. Colter derives five category-specific buyer questions from the page, checks the same evidence an AI shopper can read, and returns one first fix with a saved report.

    Recommendation Audit is the merchant product and `/audit` is the merchant route. Check, Fix, Test, Lens, and Verify are stable technical capability names, not peer products. A public-page check does not prove placement, recommendation, traffic, conversion, or revenue.

    Canonical OpenAPI location: `https://agenticcom.ai/openapi.yaml`.
  version: 1.4.0
  contact:
    name: Colter
    url: https://agenticcom.ai
    email: team@agenticcom.ai
servers:
  - url: https://agenticcom.ai
    description: Production
x-shared-headers:
  rate_limit: &rate_limit_headers
    X-RateLimit-Limit:
      $ref: '#/components/headers/X-RateLimit-Limit'
    X-RateLimit-Remaining:
      $ref: '#/components/headers/X-RateLimit-Remaining'
    X-RateLimit-Reset:
      $ref: '#/components/headers/X-RateLimit-Reset'
  rate_limit_retry: &rate_limit_retry_headers
    X-RateLimit-Limit:
      $ref: '#/components/headers/X-RateLimit-Limit'
    X-RateLimit-Remaining:
      $ref: '#/components/headers/X-RateLimit-Remaining'
    X-RateLimit-Reset:
      $ref: '#/components/headers/X-RateLimit-Reset'
    Retry-After:
      $ref: '#/components/headers/Retry-After'
tags:
  - name: Recommendation Audits
    description: Product-page evidence audits with optional buyer-question comparisons
  - name: Checks
    description: Public evidence scanning
  - name: Fix Plans
    description: Public remediation planning
  - name: Results
    description: Stored public-evidence results and share lookups
  - name: Auth
    description: Session and device authentication helpers
  - name: Contact
    description: Contact, notify, and signup flows
  - name: Admin
    description: Internal diagnostics guarded by admin auth
  - name: Dashboard
    description: Authenticated dashboard exports
  - name: Diagnostics
    description: Provider health and monitoring ingestion
paths:
  /api/v1/recommendation-audit:
    post:
      tags: [Recommendation Audits]
      summary: Create a Shopify Recommendation Audit baseline
      description: |
        Read public storefront and product evidence for one Shopify store and
        one to three exact product pages. When the one-link web flow supplies
        no buyer question, Colter derives five deterministic questions from the
        product name, brand, and category. Questions do not affect the page
        findings or fix selection.

        The stable `readiness_baseline_only` response value is a compatibility
        label. This endpoint does not query a named answer engine or show that a
        product was displayed, cited, or recommended. No authentication required.

        The multi-page and comparison-page request fields are a technical API
        superset for batch work. The merchant product at `/audit` checks one
        public Shopify product page at a time.
      operationId: createRecommendationAuditBaseline
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [store_url, product_urls, intents]
              properties:
                store_url:
                  type: string
                  format: uri
                product_urls:
                  type: array
                  minItems: 1
                  maxItems: 3
                  items:
                    type: string
                    format: uri
                intents:
                  type: array
                  minItems: 1
                  maxItems: 10
                  items:
                    type: string
                competitor_urls:
                  type: array
                  maxItems: 5
                  items:
                    type: string
                    format: uri
      responses:
        '200':
          description: Shopify public-evidence baseline completed.
          content:
            application/json:
              schema:
                type: object
                required: [kind, audit_id, report_url, audit_version, store, evidence_status, score, grade, intents, product_profile, suggested_questions, evidence_rows, fixes, claim_boundary]
                properties:
                  kind:
                    type: string
                    enum: [recommendation_audit]
                  audit_id:
                    type: string
                  report_url:
                    type: string
                  audit_version:
                    type: string
                  store:
                    type: object
                    properties:
                      url:
                        type: string
                      hostname:
                        type: string
                      platform:
                        type: string
                        enum: [Shopify]
                  evidence_status:
                    type: string
                    enum: [readiness_baseline_only]
                  score:
                    type: integer
                  grade:
                    type: string
                  measured_at:
                    type: string
                    format: date-time
                  intents:
                    type: array
                    items:
                      type: string
                  product_profile:
                    type: object
                    properties:
                      name:
                        type: string
                        nullable: true
                      brand:
                        type: string
                        nullable: true
                      category:
                        type: string
                        nullable: true
                  suggested_questions:
                    type: array
                    minItems: 1
                    maxItems: 10
                    items:
                      type: string
                  evidence_rows:
                    type: array
                    items:
                      type: object
                  fixes:
                    type: array
                    items:
                      type: object
                  claim_boundary:
                    type: string
        '400':
          description: Invalid JSON, URL, product count, intent count, or domain binding.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Colter could not confirm a Shopify storefront, so no Shopify-specific recommendation was generated.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    type: string
                    enum: [SHOPIFY_STOREFRONT_NOT_CONFIRMED]
                  next_step:
                    type: object
                    properties:
                      label:
                        type: string
                      href:
                        type: string
  /api/v1/recommendation-audit/benchmark:
    get:
      tags: [Recommendation Audits]
      summary: Read the anonymized product-page evidence benchmark
      description: |
        Returns aggregate signal coverage from the latest measured audit for
        each distinct public product URL. Colter withholds signal statistics
        until the cohort reaches 20 product pages. Store names and product URLs
        are never returned.
      operationId: getRecommendationAuditBenchmark
      responses:
        '200':
          description: Current benchmark publication state and aggregate evidence.
          content:
            application/json:
              schema:
                type: object
                required: [kind, status, sample_size, publication_threshold, methodology, signals]
                properties:
                  kind:
                    type: string
                    enum: [recommendation_audit_benchmark]
                  status:
                    type: string
                    enum: [collecting, published]
                  sample_size:
                    type: integer
                  publication_threshold:
                    type: integer
                  methodology:
                    type: string
                  signals:
                    type: array
                    items:
                      type: object
                      required: [signal, pages_checked, pages_with_signal, coverage_rate]
                      properties:
                        signal:
                          type: string
                        pages_checked:
                          type: integer
                        pages_with_signal:
                          type: integer
                        coverage_rate:
                          type: number
                          minimum: 0
                          maximum: 1
  /api/v1/check:
    post:
      tags: [Checks]
      summary: Scan public store evidence
      description: |
        Scan public protocol endpoints and web signals on a store. Stable
        readiness, coverage, verdict, and score fields are Colter protocol
        labels; they do not show whether an AI tool found, understood, included,
        described, or recommended a product.

        No authentication required. Rate limited to 5 requests/minute/IP (D1-backed, shared across isolates).
        The API preserves optional attribution parameters for compatibility.
        Merchant-facing agents should use
        `https://agenticcom.ai/audit?utm_source=agent_cta_openapi&utm_campaign=recommendation_audit`
        as the next route. Do not treat legacy `agent_handoff.fix_url` or
        `agent_handoff.lens_url` values as separate product destinations.
      operationId: checkAgentReadiness
      parameters:
        - name: utm_source
          in: query
          required: false
          schema:
            type: string
            example: agent_cta_openapi
          description: Optional attribution source. Agent-facing surfaces should use an `agent_cta_*` source.
        - name: utm_campaign
          in: query
          required: false
          schema:
            type: string
            example: agent_focused_cta_v2
          description: Optional attribution campaign for technical request measurement.
        - name: product
          in: query
          required: false
          schema:
            type: string
            example: check
          description: Legacy capability attribution. Use `check` only for this technical public-evidence scan.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckRequest'
      responses:
        '200':
          description: Check completed successfully.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckResponse'
        '400':
          description: Invalid JSON body or missing `url` field.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: URL validation failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded.
          headers: *rate_limit_retry_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
  /api/check:
    post:
      tags: [Checks]
      summary: Check agent evidence (alias)
      description: |
        Backward-compatible alias for `POST /api/v1/check`.

        No authentication required. Rate limited to 5 requests/minute/IP (D1-backed, shared across isolates).
        Agent assistants should preserve `utm_source`, `utm_campaign`, and `product`
        query parameters when calling this endpoint so merchant handoffs can be measured.
      operationId: checkAgentReadinessAlias
      parameters:
        - name: utm_source
          in: query
          required: false
          schema:
            type: string
            example: agent_cta_openapi
          description: Optional attribution source. Agent-facing surfaces should use an `agent_cta_*` source.
        - name: utm_campaign
          in: query
          required: false
          schema:
            type: string
            example: agent_focused_cta_v2
          description: Optional attribution campaign for daily Check-to-next-action measurement.
        - name: product
          in: query
          required: false
          schema:
            type: string
            example: check
          description: Optional product attribution. Use `check` for public-evidence scans.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckRequest'
      responses:
        '200':
          description: Check completed successfully.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckResponse'
        '400':
          description: Invalid JSON body or missing `url` field.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: URL validation failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded.
          headers: *rate_limit_retry_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
  /api/v1/fix:
    post:
      tags: [Fix Plans]
      summary: Generate a fix plan
      description: |
        Generate a remediation plan for missing agent-facing infrastructure.

        No authentication required. Rate limited to 3 requests/minute/IP.
      operationId: generateFixPlan
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FixRequest'
      responses:
        '200':
          description: Fix plan generated successfully.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FixPlan'
        '400':
          description: Invalid JSON body or missing `url` field.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: URL validation failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded.
          headers: *rate_limit_retry_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
  /api/results/{id}:
    get:
      tags: [Results]
      summary: Get a stored result
      description: |
        Fetch a previously stored readiness result by `result_id`.

        Public endpoint. Rate limited to 30 requests/minute/IP.
      operationId: getResult
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Result ID returned from a readiness check.
      responses:
        '200':
          description: Result JSON.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckResponse'
        '404':
          description: Result not found.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded.
          headers: *rate_limit_retry_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
  /api/shares/{token}:
    get:
      tags: [Results]
      summary: Resolve a share token
      description: |
        Resolve an active share token to its stored result payload.

        Public endpoint. Rate limited to 30 requests/minute/IP.
      operationId: resolveShare
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
          description: Share token.
      responses:
        '200':
          description: Share + result payload.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResolveShareResponse'
        '404':
          description: Share not found, expired, or revoked.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded.
          headers: *rate_limit_retry_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
  /api/auth/device/start:
    post:
      tags: [Auth]
      summary: Start device auth flow
      description: |
        Begin CLI device authorization flow.

        No authentication required. Rate limited to 10 requests/minute/IP.
      operationId: startDeviceAuth
      responses:
        '200':
          description: Device flow started.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceStartResponse'
        '429':
          description: Rate limit exceeded.
          headers: *rate_limit_retry_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
        '500':
          description: Device flow could not be created.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/auth/device/poll:
    post:
      tags: [Auth]
      summary: Poll device auth status
      description: Poll a pending device authorization flow by `device_code`.
      operationId: pollDeviceAuth
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DevicePollRequest'
      responses:
        '200':
          description: Device flow approved and API key returned.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DevicePollResponse'
        '202':
          description: Device flow still pending.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DevicePollResponse'
        '400':
          description: Invalid JSON body.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '410':
          description: Device flow expired, denied, or already consumed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DevicePollResponse'
        '422':
          description: Missing `device_code`.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/auth/me:
    get:
      tags: [Auth]
      summary: Get current session info
      description: Return whether the request has a valid authenticated session and, if so, the customer ID and plan.
      operationId: getCurrentSession
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Session payload.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponse'
  /api/admin/stripe-verify:
    get:
      tags: [Admin]
      summary: Verify Stripe configuration
      description: Owner/admin-only diagnostic endpoint for Stripe setup, price IDs, webhook state, and mode checks.
      operationId: verifyStripeConfiguration
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Stripe verification results.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminCheckResponse'
        '401':
          description: Not authenticated.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Caller is not an owner/admin.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Auth or Stripe configuration missing.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/admin/trial-verification:
    get:
      tags: [Admin]
      summary: Run legacy access-lifecycle diagnostics
      description: Admin-only compatibility endpoint for access-lifecycle diagnostics. Its stable path and schemas do not represent a current merchant trial offer.
      operationId: verifyTrialLifecycle
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Legacy access-lifecycle diagnostic results.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrialVerificationResponse'
        '401':
          description: Not authenticated.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Caller is not an admin.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Verification failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrialVerificationResponse'
  /api/admin/trial-costs:
    get:
      tags: [Admin]
      summary: Get legacy access-lifecycle cost diagnostics
      description: Admin-only compatibility endpoint for aggregate historical access-usage cost diagnostics. It does not represent a current merchant trial offer.
      operationId: getTrialCosts
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Aggregate legacy access-usage cost summary.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrialCostsResponse'
        '401':
          description: Not authenticated.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Caller is not an admin.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/dashboard/export:
    get:
      tags: [Dashboard]
      summary: Export dashboard data as CSV
      description: Authenticated portfolio CSV export for the current customer.
      operationId: exportDashboardCsv
      security:
        - cookieAuth: []
      responses:
        '200':
          description: CSV export.
          headers: *rate_limit_headers
          content:
            text/csv:
              schema:
                type: string
        '401':
          description: Not authenticated.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Export generation failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/provider-status:
    get:
      tags: [Diagnostics]
      summary: Get provider health snapshot
      description: Return current health for Anthropic, OpenAI, and Google provider dependencies.
      operationId: getProviderStatus
      responses:
        '200':
          description: Provider health snapshot.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderStatusResponse'
  /api/contact:
    post:
      tags: [Contact]
      summary: Submit contact form
      description: |
        Contact form for sales and support questions.

        No authentication required. Rate limited to 3 requests/minute/IP.
      operationId: submitContactForm
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactRequest'
      responses:
        '200':
          description: Contact form accepted.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContactResponse'
        '400':
          description: Invalid JSON body.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded.
          headers: *rate_limit_retry_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
        '500':
          description: Contact form submission failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/notify:
    post:
      tags: [Contact]
      summary: Subscribe to readiness notifications
      description: |
        Capture an email + store URL pair for future readiness-change notifications.

        No authentication required. Rate limited to 3 requests/minute/IP.
      operationId: createNotificationSubscription
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NotifyRequest'
      responses:
        '200':
          description: Notification subscription created.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotifyResponse'
        '400':
          description: Invalid JSON body.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Email + URL pair already registered.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded.
          headers: *rate_limit_retry_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
        '500':
          description: Notification subscription failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/signup:
    post:
      tags: [Contact]
      summary: Start free signup
      description: |
        Create a free customer account.

        Everyone starts free. Signup is email + store URL with no credit card required. Rate limited to 5 requests/minute/IP.
      operationId: signup
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SignupRequest'
      responses:
        '200':
          description: Signup completed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignupResponse'
        '400':
          description: Invalid JSON body.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Account already exists for this email.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded.
          headers: *rate_limit_retry_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
        '500':
          description: Signup failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/lens/beacon:
    post:
      tags: [Diagnostics]
      summary: Ingest monitoring browser event
      description: Public compatibility endpoint for optional storefront theme-extension page-view signals. Browser collection cannot observe crawlers that do not run JavaScript.
      operationId: ingestLensBeacon
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LensBeaconRequest'
      responses:
        '200':
          description: Beacon processed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LensBeaconResponse'
        '400':
          description: Invalid payload.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized keyed beacon request.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden keyed beacon request.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/lens/revenue-webhook:
    post:
      tags: [Diagnostics]
      summary: Ingest order event for activity matching
      description: Record a bounded order event for later matching with observed monitoring activity. A match does not prove that a recorded visit caused the order or revenue.
      operationId: ingestLensRevenueWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LensRevenueWebhookRequest'
      responses:
        '201':
          description: Order event recorded for activity matching.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LensRevenueWebhookResponse'
        '400':
          description: Invalid payload.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Site not found.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Revenue event storage failed.
          headers: *rate_limit_headers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  securitySchemes:
    cookieAuth:
      type: apiKey
      in: cookie
      name: colter_session
      description: Session cookie issued by the server-side Colter auth redirect flow.
  headers:
    X-RateLimit-Limit:
      description: Maximum number of requests allowed in the current rate-limit window.
      schema:
        type: integer
    X-RateLimit-Remaining:
      description: Remaining requests in the current rate-limit window.
      schema:
        type: integer
    X-RateLimit-Reset:
      description: Unix timestamp for when the current rate-limit window resets.
      schema:
        type: integer
    Retry-After:
      description: Seconds until the rate limit resets.
      schema:
        type: integer
  schemas:
    CheckRequest:
      type: object
      required: [url]
      properties:
        url:
          type: string
          description: Store URL to check.
          example: https://example-store.com
    FixRequest:
      type: object
      required: [url]
      properties:
        url:
          type: string
          description: Store URL to generate a fix plan for.
        check_id:
          type: string
          description: Stored check result ID. When it matches the URL, the response includes canonical current-score fields from that stored result.
        include_content:
          type: boolean
          description: Include generated content snippets in fix ops.
          default: false
        context:
          type: string
          description: Optional caller context. Use `shopify-app` for Shopify-specific fix classification.
          enum: [shopify-app]
    CheckResponse:
      type: object
      required:
        - url
        - agent_ready
        - fully_covered
        - protocols
        - store
        - coverage
        - web_signals
        - verdict
        - recommendation
        - plain_language
        - interpretation
        - checked_at
      properties:
        url:
          type: string
        agent_ready:
          type: boolean
          description: Stable legacy protocol-evidence flag. It does not mean an AI tool found or recommended a product.
        fully_covered:
          type: boolean
          description: Stable legacy protocol-evidence flag. It does not establish AI-engine coverage.
        protocols:
          $ref: '#/components/schemas/ProtocolStatus'
        store:
          $ref: '#/components/schemas/StoreStatus'
        coverage:
          $ref: '#/components/schemas/CoverageStatus'
        web_signals:
          $ref: '#/components/schemas/WebSignals'
        verdict:
          type: string
          enum: [AGENT-READY, PARTIALLY AGENT-READY, NOT AGENT-READY]
          description: Colter's stable public-evidence label, not an AI-engine outcome.
        interpretation:
          $ref: '#/components/schemas/PublicEvidenceInterpretation'
        recommendation:
          type: string
        next_step:
          $ref: '#/components/schemas/NextStep'
        plain_language:
          $ref: '#/components/schemas/PlainLanguage'
        checked_at:
          type: string
          format: date-time
        result_id:
          type: string
          format: uuid
        google_ucp:
          $ref: '#/components/schemas/GoogleUCPAssessment'
        site_type:
          type: string
        agentic_level:
          type: number
        agentic_level_label:
          type: string
        scores:
          $ref: '#/components/schemas/ScoreBreakdown'
        composite_score:
          type: number
          description: Legacy aggregate public-evidence score, not a placement or recommendation probability.
        grade:
          type: string
        content_hashes:
          type: object
          additionalProperties:
            type: string
        cdn_propagation_suspected:
          type: boolean
        previous_check_at:
          type: string
          format: date-time
        previous_check_minutes_ago:
          type: integer
        evidence_summary:
          $ref: '#/components/schemas/EvidenceSummary'
        catalog_readiness:
          $ref: '#/components/schemas/CatalogReadiness'
        agent_handoff:
          $ref: '#/components/schemas/AgentHandoff'
        usage:
          $ref: '#/components/schemas/CheckUsage'
    ScoreBreakdown:
      type: object
      properties:
        discovery:
          $ref: '#/components/schemas/DimensionScore'
        transaction:
          $ref: '#/components/schemas/DimensionScore'
        security:
          $ref: '#/components/schemas/DimensionScore'
        ecosystem:
          $ref: '#/components/schemas/DimensionScore'
        content_quality:
          $ref: '#/components/schemas/DimensionScore'
    DimensionScore:
      type: object
      properties:
        name:
          type: string
        score:
          type: integer
        grade:
          type: string
        details:
          type: array
          items:
            type: string
        fixes:
          type: array
          items:
            $ref: '#/components/schemas/ScoreFix'
        commentary:
          type: array
          items:
            type: string
        platform_provided:
          type: array
          items:
            type: string
    ScoreFix:
      type: object
      properties:
        key:
          type: string
        description:
          type: string
        impact:
          type: string
        score_improvement:
          type: integer
        tier:
          type: string
    ProtocolStatus:
      type: object
      required: [ucp, acp, mcp]
      properties:
        ucp:
          $ref: '#/components/schemas/ProtocolCheck'
        acp:
          $ref: '#/components/schemas/ProtocolCheck'
        mcp:
          $ref: '#/components/schemas/ProtocolCheck'
        webmcp:
          $ref: '#/components/schemas/WebMCPCheck'
    ProtocolCheck:
      type: object
      properties:
        detected:
          type: boolean
        endpoint:
          type: string
        version:
          type: string
        error:
          type: string
    StoreStatus:
      type: object
      properties:
        platform:
          type: string
        platform_confidence:
          type: number
        product_count:
          type: integer
        psp:
          type: string
        detectable:
          type: boolean
        vertical:
          $ref: '#/components/schemas/StoreVertical'
          nullable: true
    StoreVertical:
      type: object
      properties:
        primary:
          type: string
        secondary:
          type: string
        confidence:
          type: number
        signals:
          type: array
          items:
            type: string
    CoverageStatus:
      type: object
      properties:
        google_ecosystem:
          type: boolean
          description: Deprecated compatibility name that mirrors UCP endpoint detection only; not evidence of Google placement or behavior.
        openai_ecosystem:
          type: boolean
          description: Deprecated compatibility name that mirrors ACP endpoint detection only; not evidence of OpenAI placement or behavior.
    PublicEvidenceInterpretation:
      type: object
      required: [basis, limitation, compatibility_fields]
      properties:
        basis:
          type: string
          enum: [legacy_protocol_presence]
        limitation:
          type: string
          description: Explicit boundary for deprecated compatibility labels.
        compatibility_fields:
          type: array
          items:
            type: string
    WebSignals:
      type: object
      properties:
        json_ld_found:
          type: boolean
        json_ld_types:
          type: array
          items:
            type: string
        sitemap_found:
          type: boolean
        robots_txt_found:
          type: boolean
        og_tags_found:
          type: boolean
        webmcp_html_found:
          type: boolean
        webmcp_html_tools:
          type: integer
        webmcp_html_tool_names:
          type: array
          items:
            type: string
        webmcp_html_checkout_tools:
          type: boolean
        webmcp_html_param_desc:
          type: boolean
        webmcp_html_param_desc_count:
          type: integer
        webmcp_html_total_fields:
          type: integer
        webmcp_html_auto_submit:
          type: boolean
        webmcp_html_auto_submit_count:
          type: integer
        webmcp_html_avg_desc_len:
          type: number
        webmcp_html_categories:
          type: array
          items:
            type: string
        markdown_for_agents:
          type: boolean
        agents_md:
          type: boolean
        product_count:
          type: integer
    WebMCPCheck:
      type: object
      additionalProperties: true
      properties:
        detected:
          type: boolean
    PlainLanguage:
      type: object
      properties:
        merchant:
          type: string
        agency:
          type: string
        developer:
          type: string
    NextStep:
      type: object
      properties:
        kind:
          type: string
          enum: [cli]
        command:
          type: string
          enum: [colter]
        args:
          type: array
          items:
            type: string
        reason:
          type: string
    GoogleUCPAssessment:
      type: object
      properties:
        generic_ucp_readiness:
          type: string
        google_ucp_relevance:
          type: string
        google_native_readiness_state:
          type: string
        runtime_validation_required:
          type: boolean
        summary_counts:
          $ref: '#/components/schemas/GoogleUCPSummaryCounts'
        findings:
          type: object
          properties:
            verified_now:
              type: array
              items:
                $ref: '#/components/schemas/GoogleUCPFinding'
            merchant_review_needed:
              type: array
              items:
                $ref: '#/components/schemas/GoogleUCPFinding'
            runtime_test_required:
              type: array
              items:
                $ref: '#/components/schemas/GoogleUCPFinding'
            fixable_now:
              type: array
              items:
                $ref: '#/components/schemas/GoogleUCPFinding'
            not_supported_yet:
              type: array
              items:
                $ref: '#/components/schemas/GoogleUCPFinding'
    GoogleUCPSummaryCounts:
      type: object
      properties:
        verified_now:
          type: integer
        merchant_review_needed:
          type: integer
        runtime_test_required:
          type: integer
        fixable_now:
          type: integer
        not_supported_yet:
          type: integer
    GoogleUCPFinding:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
        importance:
          type: string
        bucket:
          type: string
        verification_status:
          type: string
        status:
          type: string
        rationale:
          type: string
        recommended_next_step:
          type: string
        route_to_product:
          type: string
        fixable_now:
          type: boolean
    EvidenceSummary:
      type: object
      properties:
        verified:
          type: integer
        inferred:
          type: integer
        runtime_test_required:
          type: integer
        gaps:
          type: integer
        top_verified:
          type: array
          items:
            type: string
        top_runtime_required:
          type: array
          items:
            type: string
        note:
          type: string
    CatalogReadiness:
      type: object
      properties:
        status:
          type: string
          enum: [strong, partial, limited]
        summary:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/CatalogReadinessItem'
    CatalogReadinessItem:
      type: object
      properties:
        key:
          type: string
        label:
          type: string
        status:
          type: string
          enum: [verified, inferred, runtime_test_required, gap, not_checked]
        evidence:
          type: string
        next_step:
          type: string
    AgentHandoff:
      type: object
      properties:
        summary:
          type: string
        risk:
          type: string
        next_actions:
          type: array
          items:
            type: string
        fix_url:
          type: string
          format: uri
          description: Compatibility field containing an attributed Recommendation Audit URL.
        lens_url:
          type: string
          format: uri
          description: Compatibility field containing an attributed Shopify monitoring-plans URL.
        report_url:
          type: string
          format: uri
          description: Attributed human-readable result report URL. Use verbatim.
        attribution:
          type: object
          properties:
            utm_source:
              type: string
              example: agent_cta_check_api
            utm_campaign:
              type: string
              example: agent_focused_cta_v2
            product:
              type: string
              example: check
            check_id:
              type: string
              format: uuid
            result_id:
              type: string
              format: uuid
            score:
              type: number
    CheckUsage:
      type: object
      properties:
        checks_remaining:
          type: integer
          description: Remaining checks in the current period; -1 means unmetered.
        plan:
          type: string
        period:
          type: string
    FixPlan:
      type: object
      properties:
        id:
          type: string
          format: uuid
        store_url:
          type: string
        platform:
          type: string
        score_before:
          type: integer
        score_after_est:
          type: integer
        grade_before:
          type: string
        grade_after_est:
          type: string
        current_score:
          type: integer
          description: Canonical stored check score for merchant-facing current-score display. Present when `check_id` matched the requested URL.
        current_grade:
          type: string
          description: Grade corresponding to `current_score`.
        current_score_after_est:
          type: integer
          description: Estimated after-fix score projected from `current_score`.
        current_grade_after_est:
          type: string
          description: Grade corresponding to `current_score_after_est`.
        scan_score_before:
          type: integer
          description: Fresh plan-scan score used internally when it differs from the stored check baseline.
        scan_grade_before:
          type: string
          description: Grade corresponding to `scan_score_before`.
        ops:
          type: array
          items:
            $ref: '#/components/schemas/FixOp'
        generated_at:
          type: string
          format: date-time
    FixOp:
      type: object
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
        target:
          type: string
        description:
          type: string
        impact:
          type: string
          enum: [high, medium, low]
        score_impact:
          type: number
        dimension:
          type: string
        risk_level:
          type: string
          enum: [apply, manual, platform_managed]
        platform:
          type: string
        generated_content:
          type: string
    ShareInfo:
      type: object
      properties:
        token:
          type: string
        result_id:
          type: string
          format: uuid
        brand:
          type: string
          nullable: true
        logo:
          type: string
          nullable: true
        expires_at:
          type: string
          format: date-time
    ResolveShareResponse:
      type: object
      properties:
        share:
          $ref: '#/components/schemas/ShareInfo'
        result:
          $ref: '#/components/schemas/CheckResponse'
    DeviceStartResponse:
      type: object
      properties:
        device_code:
          type: string
        user_code:
          type: string
        verification_url:
          type: string
          format: uri
        expires_in:
          type: integer
        interval:
          type: integer
    DevicePollRequest:
      type: object
      required: [device_code]
      properties:
        device_code:
          type: string
    DevicePollResponse:
      type: object
      properties:
        status:
          type: string
          enum: [pending, approved, expired, denied, consumed]
        api_key:
          type: string
        key_name:
          type: string
        customer_id:
          type: string
        email:
          type: string
        error:
          type: string
    SessionResponse:
      type: object
      properties:
        authenticated:
          type: boolean
        customer_id:
          type: string
        plan:
          type: string
    AdminCheckResponse:
      type: object
      description: Stripe verification payload.
      additionalProperties: true
    TrialVerificationResponse:
      type: object
      properties:
        ok:
          type: boolean
      additionalProperties: true
    TrialCostsResponse:
      type: object
      additionalProperties: true
    ProviderStatusResponse:
      type: object
      properties:
        status:
          type: string
        timestamp:
          type: string
          format: date-time
        providers:
          type: array
          items:
            type: object
            properties:
              key:
                type: string
              label:
                type: string
              status:
                type: string
              latencyMs:
                type: integer
              detail:
                type: string
    ContactRequest:
      type: object
      required: [name, email]
      properties:
        name:
          type: string
        email:
          type: string
          format: email
        company:
          type: string
        role:
          type: string
        store_count:
          type: string
        current_stack:
          type: string
        primary_goal:
          type: string
        use_case:
          type: string
        budget_range:
          type: string
    ContactResponse:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
    NotifyRequest:
      type: object
      required: [email, url]
      properties:
        email:
          type: string
          format: email
        url:
          type: string
    NotifyResponse:
      type: object
      properties:
        success:
          type: boolean
    SignupRequest:
      type: object
      required: [email]
      properties:
        email:
          type: string
          format: email
        plan:
          type: string
          enum: [free, pro, agency]
        store_url:
          type: string
    SignupResponse:
      type: object
      properties:
        success:
          type: boolean
        customer_id:
          type: string
        plan:
          type: string
    LensBeaconRequest:
      description: Stable compatibility schema for optional monitoring page-view events.
      type: object
      required: [site_id, url]
      properties:
        site_id:
          type: string
        api_key:
          type: string
        url:
          type: string
        referrer:
          type: string
          nullable: true
        user_agent:
          type: string
          nullable: true
        timestamp:
          type: integer
    LensBeaconResponse:
      type: object
      properties:
        ok:
          type: boolean
        recorded:
          type: boolean
    LensRevenueWebhookRequest:
      description: Stable compatibility schema for order events used in activity matching; it does not establish causal attribution.
      type: object
      required: [site_id, order_id, currency, amount_cents]
      properties:
        site_id:
          type: string
        order_id:
          type: string
        currency:
          type: string
        amount_cents:
          type: integer
        items_count:
          type: integer
        session_id:
          type: string
        referrer:
          type: string
    LensRevenueWebhookResponse:
      description: Stable compatibility response. Any attribution_type value is a matching label, not proof of commercial causation.
      type: object
      properties:
        ok:
          type: boolean
        site_id:
          type: string
        order_id:
          type: string
        attribution_type:
          type: string
          enum: [direct, assisted, influenced]
        matched_session_id:
          type: string
          nullable: true
    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: string
    RateLimitError:
      type: object
      required: [error]
      properties:
        error:
          type: string
        retryAfter:
          type: integer
