openapi: 3.0.3
info:
  title: Colter Public API
  description: |
    Colter is an AI commerce readiness platform for online stores.

    Launch surface:
    - Check: free readiness scan for any public store URL
    - Fix: remediation plan with code snippets
    - Lens: live AI agent traffic monitoring and revenue correlation
    - Test: live AI agent shopping journeys (via CLI/MCP)

    Verify is deferred to v2. Host has been removed.

    Canonical OpenAPI location: `https://agenticcom.ai/openapi.yaml`.
  version: 1.2.0
  contact:
    name: Colter
    url: https://agenticcom.ai
    email: support@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: Checks
    description: Public readiness scanning
  - name: Fix Plans
    description: Public remediation planning
  - name: Results
    description: Stored readiness 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/check:
    post:
      tags: [Checks]
      summary: Check agent readiness
      description: |
        Check whether a store is ready for AI shopping agents.

        Probes UCP, ACP, and MCP discovery endpoints, detects the store platform,
        evaluates web signals, and returns a verdict with merchant, agency, and
        developer summaries.

        No authentication required. Rate limited to 5 requests/minute/IP (D1-backed, shared across isolates).
      operationId: checkAgentReadiness
      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 readiness (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).
      operationId: checkAgentReadinessAlias
      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: Verify trial lifecycle behavior
      description: Admin-only diagnostic endpoint for free-trial lifecycle checks.
      operationId: verifyTrialLifecycle
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Trial verification 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 aggregate trial costs
      description: Admin-only diagnostic endpoint for aggregate trial usage cost reporting.
      operationId: getTrialCosts
      security:
        - cookieAuth: []
      responses:
        '200':
          description: Aggregate trial 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: |
        Enterprise lead intake form.

        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 Lens browser beacon event
      description: Public beacon ingestion endpoint for zero-code storefront monitoring installs.
      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 Lens revenue event
      description: Record merchant order-completion revenue events for Lens attribution and revenue correlation.
      operationId: ingestLensRevenueWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LensRevenueWebhookRequest'
      responses:
        '201':
          description: Revenue event recorded.
          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.
        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
        - checked_at
      properties:
        url:
          type: string
        agent_ready:
          type: boolean
        fully_covered:
          type: boolean
        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]
        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
        scores:
          $ref: '#/components/schemas/ScoreBreakdown'
        composite_score:
          type: number
        grade:
          type: string
    ScoreBreakdown:
      type: object
      properties:
        discovery:
          type: number
        transaction:
          type: number
        security:
          type: number
        ecosystem:
          type: number
        content_quality:
          type: number
    ProtocolStatus:
      type: object
      required: [ucp, acp, mcp]
      properties:
        ucp:
          $ref: '#/components/schemas/ProtocolCheck'
        acp:
          $ref: '#/components/schemas/ProtocolCheck'
        mcp:
          $ref: '#/components/schemas/ProtocolCheck'
    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
    CoverageStatus:
      type: object
      properties:
        google_ecosystem:
          type: boolean
        openai_ecosystem:
          type: boolean
    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
    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
    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
        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:
      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:
      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:
      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
