openapi: 3.1.0
info:
  title: FilingSift API
  version: 1.0.0
  description: >-
    Stable v1 REST contract for source-cited SEC filing facts, facilities,
    covenants, and filing comparisons. Candidate and machine-checked results
    are not human-verified financial or legal conclusions. Severity is not
    assigned. Webhooks and MCP are intentionally unavailable until the event
    benchmark is approved.
  contact:
    name: FilingSift Support
    email: support@filingsift.com
servers:
  - url: https://api.filingsift.com/api/v1
    description: Production
  - url: http://127.0.0.1:5000/api/v1
    description: Local development
tags:
  - { name: Contract, description: API lifecycle and schema metadata }
  - { name: Filings, description: Issuers and structured SEC filings }
  - { name: Intelligence, description: Source-cited facts and comparisons }
  - { name: Accounts, description: Customer signup, browser sessions, organization membership, API-key, and entitlement management }
  - { name: Billing, description: Proposed plan catalog and disabled-by-default commercial lifecycle }
  - { name: Administration, description: Internal platform administration; platform-admin key required }
  - { name: Intake, description: First-party website intake }
paths:
  /auth/signup:
    post:
      tags: [Accounts]
      operationId: signUpAccount
      summary: Create a free evaluation organization and expiring browser session
      requestBody:
        required: true
        content: { application/json: { schema: { $ref: "#/components/schemas/SignupRequest" } } }
      responses:
        "201": { description: Evaluation account and browser session created, content: { application/json: { schema: { $ref: "#/components/schemas/AuthSessionResponse" } } } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "409": { $ref: "#/components/responses/Conflict" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }
  /auth/login:
    post:
      tags: [Accounts]
      operationId: loginAccount
      summary: Exchange an account email and password for an expiring browser session
      requestBody:
        required: true
        content: { application/json: { schema: { $ref: "#/components/schemas/LoginRequest" } } }
      responses:
        "200": { description: Browser session created, content: { application/json: { schema: { $ref: "#/components/schemas/AuthSessionResponse" } } } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }
  /auth/logout:
    post:
      tags: [Accounts]
      operationId: logoutAccount
      summary: Revoke the current browser session
      security: [{ ApiKeyAuth: [] }]
      responses:
        "204": { description: Browser session revoked }
        "401": { $ref: "#/components/responses/Unauthorized" }
  /meta:
    get:
      tags: [Contract]
      operationId: getApiMetadata
      summary: Get the current API and schema versions
      responses:
        "200":
          description: Current contract metadata and capability gates
          headers: { X-API-Version: { $ref: "#/components/headers/ApiVersion" }, ETag: { $ref: "#/components/headers/ETag" } }
          content: { application/json: { schema: { $ref: "#/components/schemas/MetadataResponse" } } }
        "304": { $ref: "#/components/responses/NotModified" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /deprecation-policy:
    get:
      tags: [Contract]
      operationId: getDeprecationPolicy
      summary: Get the v1 compatibility and retirement policy
      responses:
        "200":
          description: Active version policy
          content: { application/json: { schema: { $ref: "#/components/schemas/DeprecationPolicyResponse" } } }
        "304": { $ref: "#/components/responses/NotModified" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /companies:
    get:
      tags: [Filings]
      operationId: listCompanies
      summary: List companies available to the public explorer
      parameters:
        - { $ref: "#/components/parameters/Limit" }
        - { $ref: "#/components/parameters/Cursor" }
        - { $ref: "#/components/parameters/Ticker" }
        - { $ref: "#/components/parameters/Cik" }
        - { $ref: "#/components/parameters/Form" }
        - { $ref: "#/components/parameters/Verification" }
        - { $ref: "#/components/parameters/FiledFrom" }
        - { $ref: "#/components/parameters/FiledTo" }
      responses:
        "200":
          description: Real filing summaries, or explicitly illustrative fallback records when the store is empty
          content: { application/json: { schema: { $ref: "#/components/schemas/CompanyListResponse" } } }
        "304": { $ref: "#/components/responses/NotModified" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /companies/{ticker}:
    get:
      tags: [Filings]
      operationId: getCompany
      summary: Get one company and its disclosure events
      parameters:
        - { $ref: "#/components/parameters/TickerPath" }
      responses:
        "200":
          description: Company explorer record
          content: { application/json: { schema: { $ref: "#/components/schemas/CompanyDetailResponse" } } }
        "304": { $ref: "#/components/responses/NotModified" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /issuers:
    get:
      tags: [Filings]
      operationId: listIssuers
      summary: List issuers represented in structured storage
      parameters:
        - { $ref: "#/components/parameters/Limit" }
        - { $ref: "#/components/parameters/Cursor" }
        - { $ref: "#/components/parameters/Ticker" }
        - { $ref: "#/components/parameters/Cik" }
        - { $ref: "#/components/parameters/Form" }
        - { $ref: "#/components/parameters/Verification" }
        - { $ref: "#/components/parameters/FiledFrom" }
        - { $ref: "#/components/parameters/FiledTo" }
      responses:
        "200":
          description: Paginated issuer resources
          content: { application/json: { schema: { $ref: "#/components/schemas/IssuerListResponse" } } }
        "304": { $ref: "#/components/responses/NotModified" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /filings:
    get:
      tags: [Filings]
      operationId: listFilings
      summary: List ingested structured filings
      parameters:
        - { $ref: "#/components/parameters/Limit" }
        - { $ref: "#/components/parameters/Cursor" }
        - { $ref: "#/components/parameters/Ticker" }
        - { $ref: "#/components/parameters/Cik" }
        - { $ref: "#/components/parameters/Form" }
        - { $ref: "#/components/parameters/Verification" }
        - { $ref: "#/components/parameters/FiledFrom" }
        - { $ref: "#/components/parameters/FiledTo" }
      responses:
        "200":
          description: Filing metadata, extraction counts, hashes, and verification state
          content: { application/json: { schema: { $ref: "#/components/schemas/FilingListResponse" } } }
        "304": { $ref: "#/components/responses/NotModified" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /filings/{accession}:
    get:
      tags: [Filings]
      operationId: getFiling
      summary: Get a complete structured filing without raw SEC HTML
      parameters:
        - { $ref: "#/components/parameters/AccessionPath" }
      responses:
        "200":
          description: Sections, tables, facts, signals, refusals, processing versions, and provenance
          content: { application/json: { schema: { $ref: "#/components/schemas/StructuredFilingResponse" } } }
        "304": { $ref: "#/components/responses/NotModified" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "404": { $ref: "#/components/responses/NotFound" }
        "413": { $ref: "#/components/responses/ResponseTooLarge" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /events:
    get:
      tags: [Intelligence]
      operationId: listEvents
      summary: List comparisons or first-filing baseline events
      parameters:
        - { $ref: "#/components/parameters/Limit" }
        - { $ref: "#/components/parameters/Cursor" }
        - { $ref: "#/components/parameters/Ticker" }
        - { $ref: "#/components/parameters/Cik" }
        - { $ref: "#/components/parameters/Form" }
        - { $ref: "#/components/parameters/Category" }
        - { $ref: "#/components/parameters/Verification" }
        - { $ref: "#/components/parameters/FiledFrom" }
        - { $ref: "#/components/parameters/FiledTo" }
        - { $ref: "#/components/parameters/EffectiveFrom" }
        - { $ref: "#/components/parameters/EffectiveTo" }
      responses:
        "200":
          description: Paginated source-cited event resources; severity remains unassigned
          content: { application/json: { schema: { $ref: "#/components/schemas/EventListResponse" } } }
        "304": { $ref: "#/components/responses/NotModified" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /facilities:
    get:
      tags: [Intelligence]
      operationId: listFacilities
      summary: List extracted revolving-credit-facility facts
      parameters:
        - { $ref: "#/components/parameters/Limit" }
        - { $ref: "#/components/parameters/Cursor" }
        - { $ref: "#/components/parameters/Ticker" }
        - { $ref: "#/components/parameters/Cik" }
        - { $ref: "#/components/parameters/Form" }
        - { $ref: "#/components/parameters/Category" }
        - { $ref: "#/components/parameters/Verification" }
        - { $ref: "#/components/parameters/FiledFrom" }
        - { $ref: "#/components/parameters/FiledTo" }
        - { $ref: "#/components/parameters/EffectiveFrom" }
        - { $ref: "#/components/parameters/EffectiveTo" }
      responses:
        "200":
          description: Paginated facility facts with exact evidence
          content: { application/json: { schema: { $ref: "#/components/schemas/FacilityListResponse" } } }
        "304": { $ref: "#/components/responses/NotModified" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /covenants:
    get:
      tags: [Intelligence]
      operationId: listCovenants
      summary: List covenant and related legal-event signals
      parameters:
        - { $ref: "#/components/parameters/Limit" }
        - { $ref: "#/components/parameters/Cursor" }
        - { $ref: "#/components/parameters/Ticker" }
        - { $ref: "#/components/parameters/Cik" }
        - { $ref: "#/components/parameters/Form" }
        - { $ref: "#/components/parameters/Category" }
        - { $ref: "#/components/parameters/Verification" }
        - { $ref: "#/components/parameters/FiledFrom" }
        - { $ref: "#/components/parameters/FiledTo" }
        - { $ref: "#/components/parameters/EffectiveFrom" }
        - { $ref: "#/components/parameters/EffectiveTo" }
      responses:
        "200":
          description: Paginated candidate covenant resources with exact source evidence
          content: { application/json: { schema: { $ref: "#/components/schemas/CovenantListResponse" } } }
        "304": { $ref: "#/components/responses/NotModified" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /filing-comparisons:
    get:
      tags: [Intelligence]
      operationId: listFilingComparisons
      summary: List differences between comparable filings
      parameters:
        - { $ref: "#/components/parameters/Limit" }
        - { $ref: "#/components/parameters/Cursor" }
        - { $ref: "#/components/parameters/Ticker" }
        - { $ref: "#/components/parameters/Cik" }
        - { $ref: "#/components/parameters/Form" }
        - { $ref: "#/components/parameters/Category" }
        - { $ref: "#/components/parameters/Verification" }
        - { $ref: "#/components/parameters/FiledFrom" }
        - { $ref: "#/components/parameters/FiledTo" }
        - { $ref: "#/components/parameters/EffectiveFrom" }
        - { $ref: "#/components/parameters/EffectiveTo" }
      responses:
        "200":
          description: Paginated standardized comparisons; baseline-only events are excluded
          content: { application/json: { schema: { $ref: "#/components/schemas/EventListResponse" } } }
        "304": { $ref: "#/components/responses/NotModified" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /account:
    get:
      tags: [Accounts]
      operationId: getAccount
      summary: Get the current organization, identity, plan, usage, companies, members, and redacted keys
      security: [{ ApiKeyAuth: [] }]
      responses:
        "200": { description: Current account overview, content: { application/json: { schema: { $ref: "#/components/schemas/AccountResponse" } } } }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /billing/catalog:
    get:
      tags: [Billing]
      operationId: getBillingCatalog
      summary: Get proposed plans and commercial availability
      description: All prices remain proposed and purchaseAvailable is false until launch gates pass.
      responses:
        "200": { description: Proposed billing catalog, content: { application/json: { schema: { $ref: "#/components/schemas/BillingCatalogResponse" } } } }
        "429": { $ref: "#/components/responses/RateLimited" }
  /billing/webhooks/provider:
    post:
      tags: [Billing]
      operationId: receiveBillingWebhook
      summary: Receive a signed event from the configured billing provider
      description: Reserved provider boundary. Production rejects it while billing is disabled.
      parameters:
        - { in: header, name: X-Billing-Signature, required: true, schema: { type: string } }
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, additionalProperties: true } } }
      responses:
        "202": { description: Verified event accepted idempotently }
        "400": { $ref: "#/components/responses/BadRequest" }
  /account/billing:
    get:
      tags: [Billing]
      operationId: getAccountBilling
      summary: Get entitlement, proposed catalog, subscription state, and invoices
      security: [{ ApiKeyAuth: [] }]
      responses:
        "200": { description: Current billing state, content: { application/json: { schema: { $ref: "#/components/schemas/AccountBillingResponse" } } } }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /account/billing/invoices:
    get:
      tags: [Billing]
      operationId: listAccountInvoices
      summary: List bounded provider-hosted invoice and receipt metadata
      security: [{ ApiKeyAuth: [] }]
      responses:
        "200": { description: Invoice history, content: { application/json: { schema: { $ref: "#/components/schemas/InvoiceListResponse" } } } }
        "401": { $ref: "#/components/responses/Unauthorized" }
  /account/billing/checkout:
    post:
      tags: [Billing]
      operationId: createBillingCheckout
      summary: Create checkout for a proposed paid plan when commercial billing is activated
      security: [{ ApiKeyAuth: [] }]
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [planKey], properties: { planKey: { type: string, enum: [builder, professional, team] } } } } }
      responses:
        "201": { description: Provider-hosted checkout session }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /account/billing/portal:
    post:
      tags: [Billing]
      operationId: createBillingPortal
      summary: Create a provider-hosted customer portal session
      security: [{ ApiKeyAuth: [] }]
      responses:
        "201": { description: Provider-hosted portal session }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /account/billing/cancel:
    post:
      tags: [Billing]
      operationId: cancelBillingSubscription
      summary: Request subscription cancellation now or at period end
      security: [{ ApiKeyAuth: [] }]
      requestBody:
        content: { application/json: { schema: { type: object, properties: { atPeriodEnd: { type: boolean, default: true } } } } }
      responses:
        "200": { description: Cancellation request accepted }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /account/audit:
    get:
      tags: [Accounts]
      operationId: listAccountAudit
      summary: List the organization's latest account and key changes
      security: [{ ApiKeyAuth: [] }]
      responses:
        "200": { description: Latest account audit entries, content: { application/json: { schema: { $ref: "#/components/schemas/AuditResponse" } } } }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /account/api-keys:
    post:
      tags: [Accounts]
      operationId: createApiKey
      summary: Create an API key whose token is returned exactly once
      security: [{ ApiKeyAuth: [] }]
      requestBody:
        required: true
        content: { application/json: { schema: { $ref: "#/components/schemas/ApiKeyCreateRequest" } } }
      responses:
        "201": { description: Key created; securely store the one-time token, content: { application/json: { schema: { $ref: "#/components/schemas/ApiKeyIssueResponse" } } } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /account/api-keys/{key_id}/rotate:
    post:
      tags: [Accounts]
      operationId: rotateApiKey
      summary: Atomically revoke an active key and issue its replacement
      security: [{ ApiKeyAuth: [] }]
      parameters: [{ $ref: "#/components/parameters/KeyIdPath" }]
      responses:
        "201": { description: Replacement token returned exactly once, content: { application/json: { schema: { $ref: "#/components/schemas/ApiKeyIssueResponse" } } } }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
  /account/api-keys/{key_id}:
    delete:
      tags: [Accounts]
      operationId: revokeApiKey
      summary: Revoke an active API key other than the key authorizing this request
      security: [{ ApiKeyAuth: [] }]
      parameters: [{ $ref: "#/components/parameters/KeyIdPath" }]
      responses:
        "200": { description: Redacted revoked-key metadata, content: { application/json: { schema: { $ref: "#/components/schemas/ApiKeyResponse" } } } }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
  /account/companies:
    post:
      tags: [Accounts]
      operationId: addAccountCompany
      summary: Add a ticker to the organization's bounded company set
      security: [{ ApiKeyAuth: [] }]
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [ticker], properties: { ticker: { type: string, pattern: '^[A-Za-z0-9.-]{1,16}$' } } } } }
      responses:
        "201": { description: Company entitlement added, content: { application/json: { schema: { $ref: "#/components/schemas/TickerResponse" } } } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "409": { $ref: "#/components/responses/Conflict" }
  /account/companies/{ticker}:
    delete:
      tags: [Accounts]
      operationId: removeAccountCompany
      summary: Remove a ticker from the organization's company set
      security: [{ ApiKeyAuth: [] }]
      parameters: [{ $ref: "#/components/parameters/TickerPath" }]
      responses:
        "204": { description: Company entitlement removed }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /account/members:
    post:
      tags: [Accounts]
      operationId: addAccountMember
      summary: Add an organization member and return an initial key once
      security: [{ ApiKeyAuth: [] }]
      requestBody:
        required: true
        content: { application/json: { schema: { $ref: "#/components/schemas/MemberCreateRequest" } } }
      responses:
        "201": { description: Membership and initial one-time key created, content: { application/json: { schema: { $ref: "#/components/schemas/MemberIssueResponse" } } } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "409": { $ref: "#/components/responses/Conflict" }
  /account/members/{user_id}:
    patch:
      tags: [Accounts]
      operationId: updateAccountMemberRole
      summary: Update a member role while preserving at least one owner
      security: [{ ApiKeyAuth: [] }]
      parameters: [{ $ref: "#/components/parameters/UserIdPath" }]
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [role], properties: { role: { $ref: "#/components/schemas/MembershipRole" } } } } }
      responses:
        "200": { description: Updated membership, content: { application/json: { schema: { $ref: "#/components/schemas/MembershipResponse" } } } }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
    delete:
      tags: [Accounts]
      operationId: removeAccountMember
      summary: Remove a member and revoke all of that member's active keys
      security: [{ ApiKeyAuth: [] }]
      parameters: [{ $ref: "#/components/parameters/UserIdPath" }]
      responses:
        "204": { description: Member removed and keys revoked }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "409": { $ref: "#/components/responses/Conflict" }
  /admin/organizations:
    get:
      tags: [Administration]
      operationId: listOrganizations
      summary: List customer organizations for internal administration
      security: [{ ApiKeyAuth: [] }]
      responses:
        "200": { description: Organization overviews, content: { application/json: { schema: { $ref: "#/components/schemas/OrganizationListResponse" } } } }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
    post:
      tags: [Administration]
      operationId: createOrganization
      summary: Create an organization, owner, membership, and initial one-time key
      security: [{ ApiKeyAuth: [] }]
      requestBody:
        required: true
        content: { application/json: { schema: { $ref: "#/components/schemas/OrganizationCreateRequest" } } }
      responses:
        "201": { description: Organization and one-time owner key created, content: { application/json: { schema: { $ref: "#/components/schemas/OrganizationIssueResponse" } } } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "409": { $ref: "#/components/responses/Conflict" }
  /admin/organizations/{organization_id}:
    patch:
      tags: [Administration]
      operationId: updateOrganization
      summary: Change an organization's active status or plan limits
      security: [{ ApiKeyAuth: [] }]
      parameters: [{ $ref: "#/components/parameters/OrganizationIdPath" }]
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, properties: { status: { type: string, enum: [active, suspended] }, planKey: { $ref: "#/components/schemas/PlanKey" } } } } }
      responses:
        "200": { description: Updated organization, content: { application/json: { schema: { $ref: "#/components/schemas/OrganizationResponse" } } } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
  /admin/audit:
    get:
      tags: [Administration]
      operationId: listGlobalAudit
      summary: List the latest cross-organization administrative audit entries
      security: [{ ApiKeyAuth: [] }]
      responses:
        "200": { description: Global audit entries, content: { application/json: { schema: { $ref: "#/components/schemas/AuditResponse" } } } }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /leads:
    post:
      tags: [Intake]
      operationId: createLead
      summary: Store a design-partner early-access request
      requestBody:
        required: true
        content: { application/json: { schema: { $ref: "#/components/schemas/LeadRequest" } } }
      responses:
        "201":
          description: Request stored
          content: { application/json: { schema: { $ref: "#/components/schemas/LeadResponse" } } }
        "202":
          description: Honeypot request accepted without storage
          content: { application/json: { schema: { $ref: "#/components/schemas/AcceptedResponse" } } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }
  /analytics/events:
    post:
      tags: [Intake]
      operationId: createAnalyticsEvent
      summary: Store an allowlisted cookie-free first-party website event
      requestBody:
        required: true
        content: { application/json: { schema: { $ref: "#/components/schemas/AnalyticsEventRequest" } } }
      responses:
        "202":
          description: Event stored
          content: { application/json: { schema: { $ref: "#/components/schemas/AcceptedResponse" } } }
        "400": { $ref: "#/components/responses/BadRequest" }
        "429": { $ref: "#/components/responses/RateLimited" }
components:
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: FilingSift account session or API key
      description: Expiring fs_session browser token or high-entropy fs_live API key. FilingSift persists only token hashes.
  parameters:
    Limit:
      in: query
      name: limit
      description: Page size. Larger pages consume more query cost.
      schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
    Cursor:
      in: query
      name: cursor
      description: Opaque nextCursor returned by the same endpoint with the same filters.
      schema: { type: string, minLength: 1, maxLength: 1000 }
    Ticker:
      in: query
      name: ticker
      schema: { type: string, pattern: '^[A-Za-z0-9.-]{1,16}$' }
    Cik:
      in: query
      name: cik
      description: One to ten digits; the API left-pads it to ten digits.
      schema: { type: string, pattern: '^\d{1,10}$' }
    Form:
      in: query
      name: form
      schema: { $ref: "#/components/schemas/Form" }
    Verification:
      in: query
      name: verification
      schema: { $ref: "#/components/schemas/VerificationState" }
    Category:
      in: query
      name: category
      schema: { $ref: "#/components/schemas/Category" }
    FiledFrom:
      in: query
      name: filed_from
      schema: { type: string, format: date }
    FiledTo:
      in: query
      name: filed_to
      schema: { type: string, format: date }
    EffectiveFrom:
      in: query
      name: effective_from
      schema: { type: string, format: date }
    EffectiveTo:
      in: query
      name: effective_to
      schema: { type: string, format: date }
    TickerPath:
      in: path
      name: ticker
      required: true
      schema: { type: string, pattern: '^[A-Za-z0-9.-]{1,16}$' }
    AccessionPath:
      in: path
      name: accession
      required: true
      schema: { type: string, pattern: '^\d{10}-\d{2}-\d{6}$' }
    KeyIdPath:
      in: path
      name: key_id
      required: true
      schema: { type: string, format: uuid }
    UserIdPath:
      in: path
      name: user_id
      required: true
      schema: { type: string, format: uuid }
    OrganizationIdPath:
      in: path
      name: organization_id
      required: true
      schema: { type: string, format: uuid }
  headers:
    ApiVersion: { description: Major API version, schema: { type: string, const: "1" } }
    ETag: { description: Strong SHA-256 entity tag for conditional GETs, schema: { type: string } }
    RateLimitLimit: { description: Request limit for the current fixed window, schema: { type: integer } }
    RateLimitRemaining: { description: Requests remaining in the current window, schema: { type: integer } }
    RateLimitReset: { description: Seconds until the current fixed window resets, schema: { type: integer } }
    RetryAfter: { description: Seconds before retrying, schema: { type: integer } }
    MonthlyRequestsRemaining: { description: Metered data requests remaining for the organization this month, schema: { type: integer } }
  responses:
    BadRequest:
      description: Invalid filter, cursor, request, or query cost
      content: { application/json: { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
    NotFound:
      description: Resource not found
      content: { application/json: { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
    Unauthorized:
      description: Missing, malformed, expired, revoked, or invalid account session/API key, or incorrect login credentials
      content: { application/json: { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
    Forbidden:
      description: Valid key lacks a required scope, role, entitlement, history window, or platform-admin status
      content: { application/json: { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
    Conflict:
      description: Requested account transition violates a current-state constraint
      content: { application/json: { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
    PaymentRequired:
      description: The requested paid plan lacks an active subscription entitlement
      content: { application/json: { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
    ServiceUnavailable:
      description: Turnstile, commercial billing, or another required service is temporarily or intentionally unavailable
      content: { application/json: { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
    NotModified:
      description: Representation matches If-None-Match; response has no body
      headers: { ETag: { $ref: "#/components/headers/ETag" }, X-API-Version: { $ref: "#/components/headers/ApiVersion" } }
    ResponseTooLarge:
      description: Representation exceeds the configured response-size limit
      content: { application/json: { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
    RateLimited:
      description: API-wide fixed-window request limit exceeded
      headers:
        Retry-After: { $ref: "#/components/headers/RetryAfter" }
        RateLimit-Limit: { $ref: "#/components/headers/RateLimitLimit" }
        RateLimit-Remaining: { $ref: "#/components/headers/RateLimitRemaining" }
        RateLimit-Reset: { $ref: "#/components/headers/RateLimitReset" }
      content: { application/json: { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
  schemas:
    SignupRequest:
      type: object
      additionalProperties: false
      required: [displayName, email, organizationName, password, acceptedTerms, turnstileToken]
      properties:
        displayName: { type: string, minLength: 1, maxLength: 120 }
        email: { type: string, format: email, maxLength: 254 }
        organizationName: { type: string, minLength: 1, maxLength: 160 }
        password: { type: string, minLength: 12, maxLength: 128, writeOnly: true }
        acceptedTerms: { type: boolean, const: true }
        turnstileToken: { type: string, minLength: 1, maxLength: 2048, writeOnly: true, description: Single-use Cloudflare Turnstile token for action auth-signup. }
    LoginRequest:
      type: object
      additionalProperties: false
      required: [email, password, turnstileToken]
      properties:
        email: { type: string, format: email, maxLength: 254 }
        password: { type: string, minLength: 1, maxLength: 128, writeOnly: true }
        turnstileToken: { type: string, minLength: 1, maxLength: 2048, writeOnly: true, description: Single-use Cloudflare Turnstile token for action auth-login. }
    AuthSession:
      type: object
      required: [token, tokenType, expiresAt, user, organization]
      properties:
        token: { type: string, pattern: '^fs_session_[A-Za-z0-9_-]{43}$', writeOnly: true }
        tokenType: { type: string, const: Bearer }
        expiresAt: { type: string, format: date-time }
        user:
          type: object
          required: [email, displayName]
          properties:
            email: { type: string, format: email }
            displayName: { type: string }
        organization:
          type: object
          required: [name, slug]
          properties:
            name: { type: string }
            slug: { type: string }
    AuthSessionResponse:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/AuthSession" }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    Form: { type: string, enum: [10-Q, 10-Q/A, 10-K, 10-K/A, 8-K, 8-K/A] }
    VerificationState: { type: string, enum: [candidate, machine_checked, verified, illustrative] }
    Category: { type: string, enum: [debt, liquidity, covenant, going_concern] }
    ApiMeta:
      type: object
      required: [schemaVersion, apiVersion]
      properties:
        schemaVersion: { type: string, const: 1.0.0 }
        apiVersion: { type: string, const: "1" }
        mode: { type: string }
        pagination: { $ref: "#/components/schemas/Pagination" }
    Pagination:
      type: object
      required: [limit, returned, hasMore, nextCursor]
      properties:
        limit: { type: integer, minimum: 1, maximum: 100 }
        returned: { type: integer, minimum: 0, maximum: 100 }
        hasMore: { type: boolean }
        nextCursor: { type: [string, "null"] }
    ErrorReason:
      type: object
      required: [category, retryable]
      properties:
        category: { type: string, enum: [request, rate_limit, response_limit, service] }
        retryable: { type: boolean }
        retryAfterSeconds: { type: integer }
        details: { type: [object, "null"], additionalProperties: true }
        maximumCost: { type: integer }
        calculatedCost: { type: integer }
        maximumBytes: { type: integer }
        actualBytes: { type: integer }
    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, reason]
          properties:
            code: { type: string }
            message: { type: string }
            reason: { $ref: "#/components/schemas/ErrorReason" }
        requestId: { type: string }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    IssuerIdentity:
      type: object
      required: [cik, ticker, name]
      properties:
        cik: { type: string, pattern: '^\d{10}$' }
        ticker: { type: string }
        name: { type: string }
    FilingIdentity:
      type: object
      required: [accession, form, filedAt, periodEnd, primaryDocument, primaryDocumentUrl, contentSha256, sourceByteLength]
      properties:
        accession: { type: string, pattern: '^\d{10}-\d{2}-\d{6}$' }
        form: { $ref: "#/components/schemas/Form" }
        filedAt: { type: string, format: date }
        periodEnd: { type: [string, "null"], format: date }
        primaryDocument: { type: string }
        primaryDocumentUrl: { type: string, format: uri }
        contentSha256: { type: string, pattern: '^[a-f0-9]{64}$' }
        sourceByteLength: { type: integer, minimum: 0 }
    Verification:
      type: object
      required: [state, checks, limitations]
      properties:
        state: { $ref: "#/components/schemas/VerificationState" }
        checks: { type: array, items: {} }
        limitations: { type: array, items: { type: string } }
    Uncertainty:
      type: object
      required: [status, reasons]
      properties:
        status: { type: string, enum: [resolved, candidate, unresolved] }
        reasons: { type: array, items: { type: string } }
    Locator:
      type: object
      description: Parser-specific bounded location; fields are additive within v1.
      additionalProperties: true
    Source:
      type: object
      required: [accession, form, evidence, verification]
      properties:
        accession: { type: string }
        form: { $ref: "#/components/schemas/Form" }
        filedAt: { type: string, format: date }
        periodEnd: { type: [string, "null"], format: date }
        section: { type: string }
        url: { type: [string, "null"], format: uri }
        evidence: { type: string }
        evidenceSha256: { type: [string, "null"] }
        locator: { oneOf: [{ $ref: "#/components/schemas/Locator" }, { type: "null" }] }
        verification: { $ref: "#/components/schemas/VerificationState" }
    ExtractionStats:
      type: object
      required: [blockCount, sectionCount, tableCount, evidenceCount]
      properties:
        blockCount: { type: integer, minimum: 0 }
        sectionCount: { type: integer, minimum: 0 }
        tableCount: { type: integer, minimum: 0 }
        evidenceCount: { type: integer, minimum: 0 }
        factCount: { type: integer, minimum: 0 }
        signalCount: { type: integer, minimum: 0 }
        refusalCount: { type: integer, minimum: 0 }
        machineCheckedFactCount: { type: integer, minimum: 0 }
        candidateFactCount: { type: integer, minimum: 0 }
    Period:
      type: object
      required: [kind, status]
      properties:
        kind: { type: string, enum: [instant, duration, unknown] }
        label: { type: [string, "null"] }
        startDate: { type: string, format: date }
        endDate: { type: string, format: date }
        durationMonths: { type: integer }
        status: { type: string }
    FinancialFact:
      type: object
      required: [factType, concept, label, value, evidence, evidenceSha256, provenance, verification, uncertainty]
      properties:
        factType: { type: string }
        concept: { type: string }
        label: { type: string }
        value: { type: number }
        reportedValue: { type: [string, "null"] }
        unit: { type: [string, "null"] }
        currency: { type: [string, "null"] }
        scale: { type: [number, "null"] }
        scaleLabel: { type: [string, "null"] }
        period: { $ref: "#/components/schemas/Period" }
        maturityYear: { type: [integer, "null"] }
        referenceRate: { type: [string, "null"] }
        instrument:
          type: object
          properties:
            reportedName: { type: string }
            canonicalKey: { type: string }
        evidence: { type: string }
        evidenceSha256: { type: string }
        provenance: { $ref: "#/components/schemas/Locator" }
        verification: { $ref: "#/components/schemas/VerificationState" }
        uncertainty: { $ref: "#/components/schemas/Uncertainty" }
    FinancingSignal:
      type: object
      required: [signalType, concept, action, evidence, evidenceSha256, locator, verification, uncertainty]
      properties:
        signalType: { type: string }
        concept: { type: string }
        action: { type: string }
        threshold: { type: [number, "null"] }
        currency: { type: [string, "null"] }
        ratio: { type: [number, "null"] }
        comparator: { type: [string, "null"], enum: [maximum, minimum, null] }
        evidence: { type: string }
        evidenceSha256: { type: string }
        locator: { $ref: "#/components/schemas/Locator" }
        verification: { $ref: "#/components/schemas/VerificationState" }
        uncertainty: { $ref: "#/components/schemas/Uncertainty" }
    Refusal:
      type: object
      required: [concept, reasonCode, reason, evidence, evidenceSha256, locator]
      properties:
        concept: { type: string }
        reasonCode: { type: string }
        reason: { type: string }
        evidence: { type: string }
        evidenceSha256: { type: string }
        locator: { $ref: "#/components/schemas/Locator" }
    Section:
      type: object
      required: [heading, text, textSha256, locator]
      properties:
        heading: { type: string }
        text: { type: string }
        textSha256: { type: string }
        locator: { $ref: "#/components/schemas/Locator" }
    ExtractedTable:
      type: object
      required: [tableIndex, rows, rowCount, columnCount, tableSha256]
      properties:
        tableIndex: { type: integer }
        rows:
          type: array
          items: { type: array, items: { type: string } }
        rowCount: { type: integer }
        columnCount: { type: integer }
        contextEvidence: { type: [string, "null"] }
        tableSha256: { type: string }
    EvidencePassage:
      type: object
      required: [category, evidence, evidenceSha256, locator, verification]
      properties:
        category: { $ref: "#/components/schemas/Category" }
        evidence: { type: string }
        evidenceSha256: { type: string }
        locator: { $ref: "#/components/schemas/Locator" }
        verification: { $ref: "#/components/schemas/VerificationState" }
    StructuredFiling:
      type: object
      required: [schemaVersion, company, filing, extraction, verification, ingestedAt]
      properties:
        schemaVersion: { type: integer, const: 1 }
        company: { $ref: "#/components/schemas/IssuerIdentity" }
        filing: { $ref: "#/components/schemas/FilingIdentity" }
        extraction:
          type: object
          required: [parserVersion, sections, tables, evidence, facts, signals, refusals, stats]
          properties:
            parserVersion: { type: string }
            sections: { type: array, items: { $ref: "#/components/schemas/Section" } }
            tables: { type: array, items: { $ref: "#/components/schemas/ExtractedTable" } }
            evidence: { type: array, items: { $ref: "#/components/schemas/EvidencePassage" } }
            facts: { type: array, items: { $ref: "#/components/schemas/FinancialFact" } }
            signals: { type: array, items: { $ref: "#/components/schemas/FinancingSignal" } }
            refusals: { type: array, items: { $ref: "#/components/schemas/Refusal" } }
            documentClassification: { type: object, additionalProperties: true }
            stats: { $ref: "#/components/schemas/ExtractionStats" }
        verification: { $ref: "#/components/schemas/Verification" }
        processing:
          type: object
          properties:
            parserVersion: { type: string }
            processingVersion: { type: string }
        ingestedAt: { type: string, format: date-time }
    FilingSummary:
      type: object
      required: [issuer, filing, extraction, verification, parserVersion, processingVersion, ingestedAt]
      properties:
        issuer: { $ref: "#/components/schemas/IssuerIdentity" }
        filing: { $ref: "#/components/schemas/FilingIdentity" }
        extraction: { $ref: "#/components/schemas/ExtractionStats" }
        verification: { $ref: "#/components/schemas/Verification" }
        parserVersion: { type: [string, "null"] }
        processingVersion: { type: [string, "null"] }
        ingestedAt: { type: string, format: date-time }
    Issuer:
      allOf:
        - { $ref: "#/components/schemas/IssuerIdentity" }
        - type: object
          required: [filingCount, forms, latestFiling, verificationStates]
          properties:
            filingCount: { type: integer, minimum: 1 }
            forms: { type: array, items: { $ref: "#/components/schemas/Form" } }
            verificationStates: { type: array, items: { $ref: "#/components/schemas/VerificationState" } }
            latestFiling:
              type: object
              required: [accession, form, filedAt, periodEnd, url]
              properties:
                accession: { type: string }
                form: { $ref: "#/components/schemas/Form" }
                filedAt: { type: string, format: date }
                periodEnd: { type: [string, "null"], format: date }
                url: { type: string, format: uri }
    Facility:
      type: object
      required: [id, issuer, facilityType, metric, category, value, source]
      properties:
        id: { type: string }
        issuer: { $ref: "#/components/schemas/IssuerIdentity" }
        facilityType: { type: string, const: revolving_credit_facility }
        metric: { type: string, enum: [revolver_capacity, revolver_drawn, revolver_availability] }
        category: { type: string, const: liquidity }
        value: { type: number }
        reportedValue: { type: [string, "null"] }
        unit: { type: [string, "null"] }
        currency: { type: [string, "null"] }
        scale: { type: [number, "null"] }
        period: { oneOf: [{ $ref: "#/components/schemas/Period" }, { type: "null" }] }
        uncertainty: { oneOf: [{ $ref: "#/components/schemas/Uncertainty" }, { type: "null" }] }
        source: { $ref: "#/components/schemas/Source" }
    Covenant:
      type: object
      required: [id, issuer, covenantType, category, action, effectiveDate, source]
      properties:
        id: { type: string }
        issuer: { $ref: "#/components/schemas/IssuerIdentity" }
        covenantType: { type: string }
        category: { $ref: "#/components/schemas/Category" }
        action: { type: string }
        threshold: { type: [number, "null"] }
        ratio: { type: [number, "null"] }
        comparator: { type: [string, "null"] }
        currency: { type: [string, "null"] }
        uncertainty: { oneOf: [{ $ref: "#/components/schemas/Uncertainty" }, { type: "null" }] }
        effectiveDate: { type: string, format: date }
        source: { $ref: "#/components/schemas/Source" }
    Comparison:
      type: object
      required: [previousAccession, currentAccession, conceptKey, method, verification]
      properties:
        previousAccession: { type: string }
        currentAccession: { type: string }
        conceptKey: { type: string }
        method: { type: string }
        verification: { $ref: "#/components/schemas/VerificationState" }
    Event:
      type: object
      required: [id, ticker, category, severity, severityStatus, headline, effectiveDate, filedAt, before, after, values, source, disclaimer]
      properties:
        id: { type: string }
        ticker: { type: string }
        cik: { type: [string, "null"] }
        category: { $ref: "#/components/schemas/Category" }
        changeType: { type: string }
        severity: { type: "null" }
        severityStatus: { type: string, const: not_assigned_no_validated_methodology }
        headline: { type: string }
        effectiveDate: { type: string, format: date }
        filedAt: { type: string, format: date-time }
        before: { type: string }
        after: { type: string }
        values: { type: object, additionalProperties: true }
        source: { $ref: "#/components/schemas/Source" }
        previousSource: { oneOf: [{ $ref: "#/components/schemas/Source" }, { type: "null" }] }
        currentSource: { oneOf: [{ $ref: "#/components/schemas/Source" }, { type: "null" }] }
        comparison: { $ref: "#/components/schemas/Comparison" }
        disclaimer: { type: string }
    Company:
      type: object
      required: [ticker, name, sector, status, summary, liquidity, debt]
      properties:
        ticker: { type: string }
        cik: { type: string, description: Present for real SEC issuers and omitted for fictional fallback companies. }
        name: { type: string }
        sector: { type: string }
        status: { type: string }
        summary: { type: string }
        liquidity: { type: object, additionalProperties: { type: [number, "null"] } }
        debt: { type: object, additionalProperties: { type: [number, "null"] } }
        intelligence: { type: object, additionalProperties: true }
        filingCount: { type: integer }
        latestFiling: { type: object, additionalProperties: true }
        events: { type: array, items: { $ref: "#/components/schemas/Event" } }
        disclaimer: { type: string }
    MetadataResponse:
      type: object
      required: [data, meta]
      properties:
        data:
          type: object
          required: [apiVersion, schemaVersion, stability, deprecationPolicy, minimumDeprecationNoticeDays, webhooks, mcp, authentication]
          properties:
            apiVersion: { type: string, const: "1" }
            schemaVersion: { type: string, const: 1.0.0 }
            stability: { type: string, const: stable }
            deprecationPolicy: { type: string }
            minimumDeprecationNoticeDays: { type: integer, const: 180 }
            webhooks: { type: string }
            mcp: { type: string }
            authentication: { type: string, const: email_password_sessions_and_api_keys_available }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    DeprecationPolicyResponse:
      type: object
      required: [data, meta]
      properties:
        data:
          type: object
          required: [currentVersion, currentSchemaVersion, status, breakingChanges, notice, lifecycleHeaders, currentSunsetDate]
          properties:
            currentVersion: { type: string, const: "1" }
            currentSchemaVersion: { type: string, const: 1.0.0 }
            status: { type: string, const: active }
            breakingChanges: { type: string }
            notice: { type: string }
            lifecycleHeaders: { type: array, items: { type: string } }
            currentSunsetDate: { type: "null" }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    CompanyListResponse: { $ref: "#/components/schemas/CompanyPage" }
    CompanyPage:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Company" } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    CompanyDetailResponse:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/Company" }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    IssuerListResponse:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Issuer" } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    FilingListResponse:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/FilingSummary" } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    StructuredFilingResponse:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/StructuredFiling" }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    EventListResponse:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Event" } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    FacilityListResponse:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Facility" } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    CovenantListResponse:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Covenant" } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    PlanKey: { type: string, enum: [evaluation, builder, professional, team, internal] }
    MembershipRole: { type: string, enum: [owner, admin, member, viewer] }
    ApiKeyScope: { type: string, enum: [filings:read, account:read, account:write, admin:write] }
    Organization:
      type: object
      required: [id, slug, name, status, planKey, createdAt]
      properties:
        id: { type: string, format: uuid }
        slug: { type: string }
        name: { type: string }
        status: { type: string, enum: [active, suspended] }
        planKey: { $ref: "#/components/schemas/PlanKey" }
        createdAt: { type: string, format: date-time }
    User:
      type: object
      required: [id, email, displayName, status, platformAdmin]
      properties:
        id: { type: string, format: uuid }
        email: { type: string, format: email }
        displayName: { type: string }
        status: { type: string, enum: [active, suspended] }
        platformAdmin: { type: boolean }
        createdAt: { type: string, format: date-time }
    Membership:
      type: object
      required: [orgId, userId, role, status]
      properties:
        orgId: { type: string, format: uuid }
        userId: { type: string, format: uuid }
        role: { $ref: "#/components/schemas/MembershipRole" }
        status: { type: string, enum: [active, removed] }
        createdAt: { type: string, format: date-time }
        user: { $ref: "#/components/schemas/User" }
    ApiKeyMetadata:
      type: object
      description: Redacted key metadata. Raw tokens and stored SHA-256 hashes are never present.
      required: [id, userId, name, prefix, scopes, status, createdAt]
      properties:
        id: { type: string, format: uuid }
        userId: { type: string, format: uuid }
        name: { type: string }
        prefix: { type: string, pattern: '^[A-Za-z0-9_-]{8}$' }
        scopes: { type: array, items: { $ref: "#/components/schemas/ApiKeyScope" } }
        status: { type: string, enum: [active, revoked, rotated] }
        createdAt: { type: string, format: date-time }
        expiresAt: { type: [string, "null"], format: date-time }
        lastUsedAt: { type: [string, "null"], format: date-time }
        revokedAt: { type: [string, "null"], format: date-time }
        rotatedToKeyId: { type: [string, "null"], format: uuid }
    Plan:
      type: object
      required: [key, label, monthlyRequests, monthlyQueryCost, companyLimit, historyDays]
      properties:
        key: { $ref: "#/components/schemas/PlanKey" }
        label: { type: string }
        monthlyRequests: { type: integer, minimum: 1 }
        monthlyQueryCost: { type: integer, minimum: 1 }
        companyLimit: { type: integer, minimum: 1 }
        historyDays: { type: integer, minimum: 1 }
    BillingPlan:
      allOf:
        - { $ref: "#/components/schemas/Plan" }
        - type: object
          required: [displayName, proposedMonthlyPriceCents, currency, purchaseAvailable]
          properties:
            displayName: { type: string }
            proposedMonthlyPriceCents: { type: integer, minimum: 0 }
            currency: { type: string, const: USD }
            purchaseAvailable: { type: boolean, const: false }
    BillingCatalog:
      type: object
      required: [modelVersion, pricingStatus, primaryMetric, providerEnabled, plans]
      properties:
        modelVersion: { type: string }
        pricingStatus: { type: string, const: proposed_not_for_sale }
        primaryMetric: { type: string, const: monitored_companies }
        guardrails: { type: array, items: { type: string } }
        eventDeliveryMeteredSeparately: { type: boolean }
        historicalBackfills: { type: string }
        redistribution: { type: string }
        providerEnabled: { type: boolean }
        plans: { type: array, items: { $ref: "#/components/schemas/BillingPlan" } }
    BillingSubscription:
      type: object
      properties:
        orgId: { type: string, format: uuid }
        planKey: { $ref: "#/components/schemas/PlanKey" }
        status: { type: string, enum: [active, trialing, past_due, incomplete, canceled, unpaid] }
        currentPeriodEnd: { type: [string, "null"], format: date-time }
        cancelAtPeriodEnd: { type: boolean }
        graceEndsAt: { type: [string, "null"], format: date-time }
    BillingAccess:
      type: object
      required: [requestedPlanKey, effectivePlanKey, status, entitled]
      properties:
        requestedPlanKey: { $ref: "#/components/schemas/PlanKey" }
        effectivePlanKey: { $ref: "#/components/schemas/PlanKey" }
        status: { type: string }
        entitled: { type: boolean }
        reason: { type: [string, "null"] }
        currentPeriodEnd: { type: [string, "null"], format: date-time }
        cancelAtPeriodEnd: { type: boolean }
        graceEndsAt: { type: [string, "null"], format: date-time }
        subscription: { oneOf: [{ $ref: "#/components/schemas/BillingSubscription" }, { type: "null" }] }
    Invoice:
      type: object
      required: [id, orgId, status, amountDueCents, amountPaidCents, currency, createdAt]
      properties:
        id: { type: string, format: uuid }
        orgId: { type: string, format: uuid }
        status: { type: string, enum: [paid, open, void] }
        amountDueCents: { type: integer, minimum: 0 }
        amountPaidCents: { type: integer, minimum: 0 }
        currency: { type: string, pattern: '^[A-Z]{3}$' }
        hostedInvoiceUrl: { type: [string, "null"], format: uri }
        receiptUrl: { type: [string, "null"], format: uri }
        periodStart: { type: [string, "null"], format: date-time }
        periodEnd: { type: [string, "null"], format: date-time }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
    BillingCatalogResponse:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/BillingCatalog" }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    AccountBillingResponse:
      type: object
      required: [data, meta]
      properties:
        data:
          allOf:
            - { $ref: "#/components/schemas/BillingAccess" }
            - type: object
              required: [catalog, invoices]
              properties:
                catalog: { $ref: "#/components/schemas/BillingCatalog" }
                invoices: { type: array, items: { $ref: "#/components/schemas/Invoice" } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    InvoiceListResponse:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Invoice" } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    MonthlyUsage:
      type: object
      required: [orgId, month, requestCount, queryCost, responseBytes]
      properties:
        orgId: { type: string, format: uuid }
        month: { type: string, format: date }
        requestCount: { type: integer, minimum: 0 }
        queryCost: { type: integer, minimum: 0 }
        responseBytes: { type: integer, minimum: 0 }
    Identity:
      type: object
      required: [keyId, userId, email, displayName, role, scopes, platformAdmin]
      properties:
        keyId: { type: string, format: uuid }
        userId: { type: string, format: uuid }
        email: { type: string, format: email }
        displayName: { type: string }
        role: { $ref: "#/components/schemas/MembershipRole" }
        scopes: { type: array, items: { $ref: "#/components/schemas/ApiKeyScope" } }
        platformAdmin: { type: boolean }
    AccountOverview:
      type: object
      required: [organization, plan, members, companies, keys, usage]
      properties:
        organization: { $ref: "#/components/schemas/Organization" }
        plan: { $ref: "#/components/schemas/Plan" }
        members: { type: array, items: { $ref: "#/components/schemas/Membership" } }
        companies: { type: array, items: { type: string } }
        keys: { type: array, items: { $ref: "#/components/schemas/ApiKeyMetadata" } }
        usage: { $ref: "#/components/schemas/MonthlyUsage" }
    AuditEntry:
      type: object
      required: [id, action, targetType, metadata, occurredAt]
      properties:
        id: { type: string, format: uuid }
        orgId: { type: [string, "null"], format: uuid }
        actorUserId: { type: [string, "null"], format: uuid }
        actorKeyId: { type: [string, "null"], format: uuid }
        action: { type: string }
        targetType: { type: string }
        targetId: { type: [string, "null"] }
        fingerprint: { type: [string, "null"], description: HMAC pseudonymous client identifier }
        metadata: { type: object, additionalProperties: true }
        occurredAt: { type: string, format: date-time }
    ApiKeyCreateRequest:
      type: object
      required: [name]
      properties:
        name: { type: string, minLength: 1, maxLength: 120 }
        userId: { type: string, format: uuid }
        scopes: { type: array, minItems: 1, uniqueItems: true, items: { $ref: "#/components/schemas/ApiKeyScope" } }
    MemberCreateRequest:
      type: object
      required: [email, displayName, role]
      properties:
        email: { type: string, format: email }
        displayName: { type: string, minLength: 1, maxLength: 120 }
        role: { $ref: "#/components/schemas/MembershipRole" }
    OrganizationCreateRequest:
      type: object
      required: [name, slug, ownerEmail, ownerName]
      properties:
        name: { type: string, minLength: 1, maxLength: 160 }
        slug: { type: string, pattern: '^[a-z][a-z0-9-]{1,62}[a-z0-9]$' }
        ownerEmail: { type: string, format: email }
        ownerName: { type: string, minLength: 1, maxLength: 120 }
        planKey: { $ref: "#/components/schemas/PlanKey" }
    AccountResponse:
      type: object
      required: [data, meta]
      properties:
        data:
          allOf:
            - { $ref: "#/components/schemas/AccountOverview" }
            - type: object
              required: [identity]
              properties: { identity: { $ref: "#/components/schemas/Identity" } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    AuditResponse:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/AuditEntry" } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    ApiKeyIssueResponse:
      type: object
      required: [data, meta]
      properties:
        data:
          type: object
          required: [key, token, oneTimeDisplay]
          properties:
            key: { $ref: "#/components/schemas/ApiKeyMetadata" }
            token: { type: string, readOnly: true, description: Returned once; store securely. }
            oneTimeDisplay: { type: boolean, const: true }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    ApiKeyResponse:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/ApiKeyMetadata" }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    TickerResponse:
      type: object
      required: [data, meta]
      properties:
        data: { type: object, required: [ticker], properties: { ticker: { type: string } } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    MembershipResponse:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/Membership" }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    MemberIssueResponse:
      type: object
      required: [data, meta]
      properties:
        data:
          type: object
          required: [membership, key, token, oneTimeDisplay]
          properties:
            membership: { $ref: "#/components/schemas/Membership" }
            key: { $ref: "#/components/schemas/ApiKeyMetadata" }
            token: { type: string, readOnly: true, description: Returned once; deliver securely. }
            oneTimeDisplay: { type: boolean, const: true }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    OrganizationResponse:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/Organization" }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    OrganizationListResponse:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/AccountOverview" } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    OrganizationIssueResponse:
      type: object
      required: [data, meta]
      properties:
        data:
          type: object
          required: [organization, user, membership, key, token, oneTimeDisplay]
          properties:
            organization: { $ref: "#/components/schemas/Organization" }
            user: { $ref: "#/components/schemas/User" }
            membership: { $ref: "#/components/schemas/Membership" }
            key: { $ref: "#/components/schemas/ApiKeyMetadata" }
            token: { type: string, readOnly: true, description: Returned once; deliver securely. }
            oneTimeDisplay: { type: boolean, const: true }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    LeadRequest:
      type: object
      required: [name, email, company, workflow, pilot, turnstileToken]
      properties:
        name: { type: string, minLength: 1, maxLength: 120 }
        email: { type: string, format: email, maxLength: 254 }
        company: { type: string, minLength: 1, maxLength: 200 }
        workflow:
          type: string
          enum: [Credit or covenant monitoring, Investment research, Financial data product, AI or agent development, Other]
        coverage: { type: string, maxLength: 2000 }
        pilot: { type: string, enum: [Ready for a paid pilot, Open to a paid pilot after validation, Researching for later] }
        website: { type: string, maxLength: 200, description: Honeypot; human users leave blank. }
        turnstileToken: { type: string, minLength: 1, maxLength: 2048, writeOnly: true, description: Single-use Cloudflare Turnstile token for action lead. }
    AnalyticsEventRequest:
      type: object
      required: [event, path]
      properties:
        event:
          type: string
          enum: [page_view, explorer_loaded, explorer_fallback, design_partner_form_started, design_partner_form_submitted, design_partner_form_failed]
        path: { type: string, maxLength: 500, pattern: '^/' }
        properties: { type: object, additionalProperties: { oneOf: [{ type: string, maxLength: 200 }, { type: number }, { type: boolean }] } }
    LeadResponse:
      type: object
      required: [data, meta]
      properties:
        data:
          type: object
          required: [id, receivedAt]
          properties:
            id: { type: string }
            receivedAt: { type: string, format: date-time }
        meta: { $ref: "#/components/schemas/ApiMeta" }
    AcceptedResponse:
      type: object
      required: [data, meta]
      properties:
        data:
          type: object
          required: [received]
          properties: { received: { type: boolean, const: true } }
        meta: { $ref: "#/components/schemas/ApiMeta" }
