openapi: 3.1.1
jsonSchemaDialect: https://json-schema.org/draft/2020-12/schema
x-programmable-partner-credentials:
  schemaVersion: programmable.partner-public-contract.v1
  status: live
  environmentVariable: PROGRAMMABLE_API_KEY
  credentialKinds:
    - root
    - subkey
  canonicalV3LaunchRoutes: true
  launchScopes:
    - custom-launch:create
    - custom-launch:read
  rootOnlyScope: partner-subkeys:manage
  subkeyAdminRoutes:
    - GET /v1/partner/subkeys
    - POST /v1/partner/subkeys
    - POST /v1/partner/subkeys/{subkeyId}/rotate
    - DELETE /v1/partner/subkeys/{subkeyId}
  maximumSubkeyDepth: 1
  subkeyScopesAndBudgetsCannotExceedRoot: true
  subkeyExpiryCannotExceedRoot: true
  permitReissueDispositionCredentialKind: wallet-only
  metadataPolicySameAsWalletKeys: true
  controllerWallet:
    walletKey: must-equal-key-wallet-binding
    partnerCredential: selected-by-exact-request
    mustReviewSignAndBroadcast: true
  launchHistoryVisibility:
    root: all-partner-attributed-root-and-subkey-launches
    subkey: stable-subkey-lineage-only
    rootAggregatesSubkeys: true
    rotationPreservesLineageHistory: true
    newDistinctSubkeyStartsIsolatedLineage: true
    revokedCredentialCanAuthenticate: false
  secretDelivery: issue-and-rotation-response-only
  callerSuppliedAttributionAccepted: false
  attributionSource: authenticated-partner-api-key
  attributionIsVerificationOrSafetyClaim: false
  walletSigningAuthority: false
  walletBroadcastAuthority: false
  gateBypassAuthority: false
  adminProvisioning:
    authentication: website-bff-assertion-v2
    authorization: server-configured-privy-user-wallet-pair-allowlist
    clientMaySelfAuthorize: false
info:
  title: Programmable Developer API
  version: 2.0.0
  summary: One versioned integration for Programmable Classic and Custom launches.
  description: |
    Read-only discovery for terminals, scanners, explorers, bots, wallets and apps.

    Classic discovery is live on Ethereum for historical V3 and current V4 only.
    Classic V1 and V2 remain inactive manifest history and are not scanned.
    Manifest-following consumers receive V4 through a normal refresh without a
    client code or address change. The hosted Classic baseline is the complete
    paginated `https://programmable.market/api/explore` catalog, validated
    through schema, scope, evidence and identity commitments. It currently
    reports Envio deployment `production-6157d22`; legitimate deployment
    revisions do not require code changes. The retired HTTP 410 legacy source
    is not used. Custom discovery remains separate and includes Registry-verified and
    finalized Router-stamped launches. The Router is provenance and transport,
    not a public category. The separate authenticated Custom Launch
    API V1 at `https://api.programmable.market` keeps provenance reads and status
    live, but POST is read-only and returns nonretryable
    `409 CUSTOM_LAUNCH_V1_READ_ONLY`; this Developer v2 service also remains read
    only. Custom Launch API V2 retains authenticated historical reads, but POST
    is read-only and returns nonretryable `409 CUSTOM_LAUNCH_V2_READ_ONLY`.
    Custom Launch API V3 profile `3.3.0` is the sole fresh-submission route for
    exact project-owned token and hook artifacts. It accepts existing
    wallet keys plus approved partner roots and bounded one-level subkeys on the
    same V3 routes and under the same server-side policy. A root alone manages
    subkeys; a child cannot manage credentials, exceed root scopes, budgets, or
    expiry, supply its own attribution, or bypass admission. Root history
    aggregates its attributed child launches; child history is lineage-bound,
    rotation preserves lineage, and a distinct child starts a separate lineage.
    The API server, not a CLI, LLM, or client, decides authorization after the
    exact static admission baseline and pinned Router simulation. Missing or
    unavailable runtime behavior evidence leaves related claims unverified;
    an authenticated executed negative blocks wallet handoff.
    This is not an arbitrary-hook safety or universal fee-enforcement claim.
    Its existing `publicSubmissionStatus` field continues to describe the
    closed legacy intake; `customLaunchApiStatus` describes the retained
    V1/V2 reads plus the active V3 launch API. Status keeps `customLaunchApi` as the V1 compatibility
    object and publishes `currentCustomLaunchCreate` as the unambiguous active
    V3 create pointer.
    Stock-Paired launches are excluded from active API v2 Developer discovery
    and scanning. The immutable v1 compatibility snapshot retains frozen
    Stock-Paired history without reactivating Stock scanning.
    `category: custom` is the stable product taxonomy, not a claim of Registry
    acceptance. Consumers must inspect
    `extensions["programmable/classification"].basis` and verify the Registry
    or canonical-Router evidence named by that basis.

    Within v2, existing fields keep their type and meaning. New deployments,
    capabilities and market kinds are additive. Consumers must ignore unknown
    fields and keep unknown market kinds discoverable, even when they cannot
    chart, quote or execute them.
  license:
    name: MIT
    identifier: MIT
  contact:
    name: Programmable
    url: https://developers.programmable.family/
externalDocs:
  description: Programmable Developer Platform documentation
  url: https://developers.programmable.family/
servers:
  - url: https://developers.programmable.family
    description: Production
tags:
  - name: Discovery
    description: Stable service and deployment discovery.
  - name: Launches
    description: Normalized Classic and Custom launch records.
  - name: Compatibility
    description: Compatibility output for existing token-list consumers.
security: []
paths:
  /.well-known/programmable.json:
    get:
      tags: [Discovery]
      operationId: getProgrammableDiscovery
      summary: Discover the Programmable Developer API
      description: |
        Returns stable links to the v2 API, deployment manifest, schemas,
        OpenAPI document and documentation. Integrators should begin here and
        must resolve deployment addresses from the manifest rather than
        hardcoding them.
      responses:
        "200":
          description: Stable discovery document.
          headers:
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
            ETag:
              $ref: "#/components/headers/ETag"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WellKnownDocument"
        "304":
          $ref: "#/components/responses/NotModified"
        "429":
          $ref: "#/components/responses/RateLimited"
        "503":
          $ref: "#/components/responses/Unavailable"

  /v3/finalized-custom-launches:
    get:
      servers:
        - url: https://api.programmable.market
          description: Production Custom Launch API read surface
      tags: [Launches]
      operationId: listFinalizedCustomLaunchMetadataV3
      summary: List finalized V3 project metadata
      description: |
        Returns the bounded, unauthenticated
        `finalized-v3-project-metadata-ledger` snapshot. It contains finalized
        metadata-bearing V3 rows under their original contracts, including
        retained `3.2.0` and current `3.3.0` rows. This route belongs to the separate Custom
        Launch API host, not the Developer read API host. Follow every opaque
        cursor to complete one snapshot. Items exclude pending requests,
        metadata-absent historical resources, controller addresses, credentials,
        and request bytes. Every successful page requires `schemaVersion`,
        `generatedAt`, `launches`, `nextCursor`, and `quality`. A `complete`
        quality status means every source row was published; `partial` means
        one or more invalid finalized rows were quarantined. Published plus
        quarantined rows equals the source row count, with one row-indexed
        `FINALIZED_ROW_QUARANTINED` diagnostic per quarantined row.
        `resourceId` is not Router identity; bind `routerLaunchId` to the
        matching canonical Router event token, hook, PoolManager, and pool ID.
        Read each item's immutable `launchProfileVersion` before interpreting
        its metadata contract; never infer the originating profile from null or
        present presentation fields.
        Read canonical display fields from `projectMetadata`: declared token
        name and symbol, presentation description, image, website, and X link.
        Treat name and symbol as matching onchain token metadata only when
        `tokenMetadataReadback.status` reports the exact matching state; retain
        mismatch and unavailable states. New current-profile submissions must
        provide complete description, image, website, and X metadata, while
        historical finalized records remain visible when an older snapshot is
        incomplete.

        An optional `launches[].partnerAttribution` is the immutable server-owned
        `programmable.launch-partner-attribution.v1` snapshot derived from the
        authenticated partner API principal. A Developer launch projection may
        expose the same object as `launchedVia`; clients cannot set either field
        in a create request. This is display attribution, not the economic
        `partner` object or Registry `provider` evidence. Creator presentation
        remains untrusted display data and is not onchain token identity,
        safety, liquidity, tradeability, provider indexing, or external
        verification evidence. Publication cannot guarantee GMGN, Dexscreener,
        or another third party will ingest the item or assign a safe label.
      parameters:
        - name: limit
          in: query
          required: false
          description: Page size. Defaults to 10. Duplicate parameters are rejected.
          schema:
            type: integer
            minimum: 1
            maximum: 25
            default: 10
        - name: cursor
          in: query
          required: false
          description: Opaque cursor from the previous page. Duplicate parameters are rejected.
          schema:
            type: string
            minLength: 1
      responses:
        "200":
          description: Finalized Custom launch metadata, newest first.
          headers:
            Cache-Control:
              required: true
              schema:
                const: public, max-age=15, stale-while-revalidate=300
          content:
            application/json:
              schema:
                $ref: https://programmable.market/openapi/custom-launch-v3.json#/components/schemas/FinalizedCustomLaunchMetadataListV1
        "400":
          description: Invalid or duplicated pagination parameter (`INVALID_PAGINATION`).
        "503":
          description: Finalized V3 metadata source is unavailable (`CUSTOM_LAUNCH_V3_UNAVAILABLE`).

  /api/v2/status:
    get:
      tags: [Discovery]
      operationId: getStatus
      summary: Read API and data-pipeline status
      description: |
        Reports service availability separately from Classic discovery,
        feed freshness, the Custom Launch API V1 compatibility state, the
        additive `currentCustomLaunchCreate` V3 write pointer, and the
        exact retained Fee-Enforced V2 historical profile, the retained gated
        Direct Native Hook Graph V1 preview, the retained historical V2 descriptor,
        and the active additive V3 general-lane descriptor for the public Custom
        Launch API V3 route. A successful HTTP response is not itself proof that indexed
        data is current, that a fee is enforced, that source is an exact match,
        or that a contract was audited.
      parameters:
        - $ref: "#/components/parameters/ChainIdQuery"
      responses:
        "200":
          description: Current service and feed status.
          headers:
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Status"
        "304":
          $ref: "#/components/responses/NotModified"
        "429":
          $ref: "#/components/responses/RateLimited"
        "503":
          $ref: "#/components/responses/Unavailable"

  /api/v2/manifest:
    get:
      tags: [Discovery]
      operationId: getDeploymentManifest
      summary: Resolve Ethereum current and historical deployments
      description: |
        Compatibility alias for `/api/v2/manifests/1`. New integrations should
        resolve the selected chain through the per-chain manifest route.
        Returns active, inactive historical and prelaunch chain/deployment profiles.
        Active Classic discovery uses only the enabled V3 and V4 entries. V1 and
        V2 remain inactive compatibility history and are not scanned; Stock is
        excluded. Generic manifest- and Router-first consumers discover V4
        through a manifest refresh without a client code or address change.
        New EVM chains and Registry generations are added here so clients do not
        need a code change. A prelaunch Registry carries null address and start-block
        fields; clients must never replace those nulls with guessed values.
        `launchStampRouter` is the independent provenance-only trust root for
        Router-stamped Classic and Custom launches, not a public category. The frozen ABI,
        its hosted URL and published ABI-file hash, pinned source/extraction reference, events,
        indexed layouts, getter selectors, enum values and sole atomic selector are published. Its
        separate `deploymentEvidence` object publishes the finalized Ethereum
        deployment, runtime identities and observed immutable getters. Resolve
        every address, start block, runtime identity and finality requirement from
        the manifest. `/launchStampRouter/canaryEvidence` publishes the finalized
        `CustomGraph` vector, while `/launchStampRouter/classicCanaryEvidence`
        publishes separate exact finalized Classic V4 evidence.
        `canaryEvidence.routeCoverage.classicOnchainCanary` is true. Direct verification uses only the
        manifest-listed chain and Router through Ethereum JSON-RPC; historical
        launches are outside Router V1.
        Universal detection uses token or `(PoolManager, PoolId)` and then the
        record's `LaunchKindV1` value. The shared Classic hook is not a launch
        identity and must not classify a Classic launch. Only a consistent
        record from the exact canonical Router qualifies through Router V1.
        `customFeeEnforcedLaunchProfileV2` is a separate retained historical
        launch profile, not Custom Registry Generation 2 and not a third public
        category. Its exact revision and hash are authoritative. Publication
        does not imply named-terminal adoption. Launch submission is not part of
        this read-only API. Custom Launch API V1 POST is also read-only and returns
        nonretryable `409 CUSTOM_LAUNCH_V1_READ_ONLY`; V2 authenticated POST is
        also read-only and returns nonretryable `409 CUSTOM_LAUNCH_V2_READ_ONLY`.
        Only metadata-bound V3 profile `3.3.0` accepts fresh submissions.
        The gated Direct Native Hook Graph V1 preview remains available as
        optional `directNativeHookGraphProfileV1` contract data for compatibility.
        The additive `directNativeHookGraphProfileV2` descriptor is the retained
        historical Custom Launch API V3 contract for project-owned token and hook
        artifacts in exact 3–16-target graphs. It supports every valid Uniswap v4
        permission mask and the `none`, exact wallet transaction value, and
        EIP-3009 funding modes. Each accepted launch still requires its own exact
        graph conformance receipt. The additive `directNativeHookGraphProfileV3`
        descriptor publishes the active general lane. Its deterministic exact-source,
        compiler and graph baseline applies role-aware blocking rules; every
        unmatched finding remains a bound visible warning without a project exception.
        No blocking finding makes the exact request eligible for mandatory Router
        simulation; it does not authorize a launch. A blocking finding returns
        `action_required`. The API server, never a CLI, LLM, or client, decides
        authorization after the exact static admission baseline and pinned
        Router simulation. Not-configured or unavailable runtime behavior
        evidence leaves related claims unverified and does not itself block
        handoff. An authenticated executed negative returns
        `BEHAVIOR_EVIDENCE_NOT_VERIFIED` and blocks wallet handoff. A
        worker-private exact permit may exist only for pinned simulation. Public `simulating`
        and `failed` output keeps permit and wallet-transaction fields null. Its
        `api.agentIntegration` object links the canonical
        machine-readable remediation catalog and existing-project guide. Agents
        resolve returned finding codes there, repair and rebuild the exact
        project, then submit a new exact request; there is no project allowlist
        or legacy GitHub fallback. The linked pack-config schema defines
        `programmable.eip3009-authorization-patch.v2` with static
        `nonceArgumentPath`, `rArgumentPath`, `sArgumentPath`, and
        `vArgumentPath`; v1 remains compatible for exact retries. New profile
        `3.3.0` requests require
        `programmable.project-metadata.v1`; the declaration hash is bound into
        the graph hash, prepared resource, launch ID, and wallet-reviewed Router
        transaction. `programmable.project-token-metadata-binding.v1` records
        deterministic name and symbol extraction, but finalized token metadata
        still requires post-deployment readback. Descriptions, images, and links
        remain untrusted display data and never authorize execution. The
        canonical V3 resource always carries immutable `launchProfileVersion`;
        `projectMetadata` and `projectMetadataHash` are non-null for metadata-bound
        `3.2.0` and `3.3.0`, and null for retained `2.0.0`, `3.0.0`, and `3.1.0`
        resources. Profile `3.2.0` retains its legacy nullable-image metadata
        contract; fresh `3.3.0` packs use the stricter complete policy.
        The canonical conditional schema remains in the linked Custom Launch V3
        OpenAPI rather than being redefined here. Admission is
        not an audit or a safety, honeypot, liquidity,
        tradeability, or fee-behavior guarantee. Finalized consistent Router launches may enter
        the launch feed; token-list projection additionally requires a token
        identity. Neither descriptor is a third public category, and this
        Developer V2 OpenAPI remains read-only: it describes but does not expose
        the authenticated V3 write routes or executable wallet calldata.
      responses:
        "200":
          description: Current deployment manifest.
          headers:
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Manifest"
        "304":
          $ref: "#/components/responses/NotModified"
        "429":
          $ref: "#/components/responses/RateLimited"
        "503":
          $ref: "#/components/responses/Unavailable"

  /api/v2/manifests/{chainId}:
    get:
      tags: [Discovery]
      operationId: getChainDeploymentManifest
      summary: Resolve one chain-bound deployment manifest
      description: |
        Returns a manifest whose root `chainId` and `caip2` match the path.
        Ethereum remains live through chain 1. Robinhood Chain 4663 is published
        as `planned` until exact Programmable Router deployment, runtime, start
        block, finalized canary, backend quality, CLI and OpenAPI release evidence
        is available. Planned null deployment fields must never be replaced with
        guessed addresses. A listed chain is discoverable; its feed quality and
        promotion status determine whether absence is authoritative.
      parameters:
        - $ref: "#/components/parameters/ChainIdPath"
      responses:
        "200":
          description: Exact chain-bound deployment manifest.
          headers:
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Manifest"
        "304":
          $ref: "#/components/responses/NotModified"
        "404":
          description: No manifest is published for the requested chain (`CHAIN_NOT_SUPPORTED`).
        "429":
          $ref: "#/components/responses/RateLimited"
        "503":
          $ref: "#/components/responses/Unavailable"

  /api/v2/launches:
    get:
      tags: [Launches]
      operationId: listLaunches
      summary: List normalized Programmable launches
      description: |
        Returns Classic and Custom launches through one cursor-paginated
        envelope. Classic covers manifest-enabled V3 and V4 only. Its hosted
        baseline is the fully traversed paginated
        `https://programmable.market/api/explore` catalog, validated through
        schema, scope, evidence and identity commitments and currently reporting
        Envio deployment `production-6157d22`; the retired HTTP 410 legacy source
        is not queried.
        A launch can contain zero, one or several markets. The API
        never fabricates a pool for a token without one. Token amounts are raw
        decimal integer strings. Persist `page.resumeCursor` after processing a
        completed poll and pass it back as `after`. `page.nextCursor` is only
        for continuing the current page traversal. `after` and `cursor` must
        not be sent together. Coverage gaps do not hide recognized records:
        the response remains HTTP 200 with `status: degraded` or `unavailable`.
        Finalized canonical-Router Custom records are an independent provenance
        lane from the Custom Registry. Their token and pool identities remain
        present through a bounded source whose canonical commitment is
        recomputed before publication, plus a digest-pinned last-known-good
        fallback for source outages. Absent fee or supply evidence stays
        explicitly unavailable. The feed remains degraded and absence is not
        authoritative until Router coverage is current. A Router record's
        absent Registry fee policy is semantically valid only when its exact
        entry and source-boundary digests are members of the accepted source
        snapshot; source-shaped JSON alone is never sufficient. Finalized
        Router identities are joined to the Custom Launch API finalized
        metadata ledger only when launch ID, Router, token, hook, PoolManager,
        pool ID, transaction, block and log index all match and the declared
        metadata hash recomputes exactly. The resulting `token.metadata` stays
        `creator-declared`; its immutable source binding is retained in
        `extensions["programmable/finalized-project-metadata-v1"]`. A metadata
        source outage or mismatch leaves those display fields unavailable and
        never removes the recognized Router identity. Finalized
        partner-launched Custom records may add `launchedVia`, an exact
        read-only projection of the Custom Launch API's server-owned
        `partnerAttribution` snapshot. It never changes `category`, economic
        fee policy, provider evidence, review, safety, or index status.
        Promoted Robinhood V4 records also retain the backend's complete
        source-verification status in
        `extensions["programmable/backend-finalized-v4"].sourceVerification`.
        This status is independent from Router provenance and launch finality.
        Only a durable Sourcify V2 exact result may name `sourcify-v2` and carry
        an evidence digest; Blockscout observations alone never establish an
        exact match. Aggregate precedence is `needs_attention`, then `retrying`,
        then `queued`, unless every component is `exact_match`.
      parameters:
        - $ref: "#/components/parameters/ChainIdQuery"
        - $ref: "#/components/parameters/CategoryQuery"
        - $ref: "#/components/parameters/AfterQuery"
        - $ref: "#/components/parameters/CursorQuery"
        - $ref: "#/components/parameters/LimitQuery"
      responses:
        "200":
          description: Cursor-paginated launch feed, including bounded recognized records when quality is degraded or unavailable.
          headers:
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LaunchFeed"
        "304":
          $ref: "#/components/responses/NotModified"
        "400":
          $ref: "#/components/responses/BadRequest"
        "429":
          $ref: "#/components/responses/RateLimited"
        "503":
          $ref: "#/components/responses/Unavailable"

  /api/v2/launches/{chainId}/{tokenAddress}:
    get:
      tags: [Launches]
      operationId: getLaunchByToken
      summary: Get one launch by chain and token address
      description: |
        Returns the canonical normalized record for one token. Name and symbol
        are display metadata and are never used as identity.
      parameters:
        - $ref: "#/components/parameters/ChainIdPath"
        - $ref: "#/components/parameters/TokenAddressPath"
      responses:
        "200":
          description: Normalized launch record.
          headers:
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Launch"
        "304":
          $ref: "#/components/responses/NotModified"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "503":
          $ref: "#/components/responses/Unavailable"

  /api/v2/launches/{launchId}:
    get:
      tags: [Launches]
      operationId: getLaunchById
      summary: Get one Ethereum launch by canonical launch ID
      description: |
        Ethereum compatibility alias for `/api/v2/chains/1/launches/{launchId}`.
        New multi-chain integrations must use the chain-qualified route because
        a launch ID is not a global cross-chain identity.
      parameters:
        - $ref: "#/components/parameters/LaunchIdPath"
      responses:
        "200":
          description: Normalized launch record.
          headers:
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Launch"
        "304":
          $ref: "#/components/responses/NotModified"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "503":
          $ref: "#/components/responses/Unavailable"

  /api/v2/chains/{chainId}/launches/{launchId}:
    get:
      tags: [Launches]
      operationId: getLaunchByIdForChain
      summary: Get one launch by chain and canonical launch ID
      description: |
        Returns any Programmable launch on one published EVM chain, including
        project-only and multi-token launches that do not have one canonical
        ERC-20 lookup address. Chain ID and launch ID are both identity inputs.
      parameters:
        - $ref: "#/components/parameters/ChainIdPath"
        - $ref: "#/components/parameters/LaunchIdPath"
      responses:
        "200":
          description: Normalized launch record.
          headers:
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Launch"
        "304":
          $ref: "#/components/responses/NotModified"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "503":
          $ref: "#/components/responses/Unavailable"

  /api/v2/token-list:
    get:
      tags: [Compatibility]
      operationId: getTokenList
      summary: Read the token-list compatibility projection
      description: |
        Returns a token-list-compatible projection for integrations that need
        token identity and metadata. The top-level `status` reports projection
        quality, and recognized complete token identities remain present when
        unrelated coverage or market enrichment is incomplete. This includes
        finalized canonical-Router Custom tokens with complete token identity.
        Use the launch feed for markets, capabilities, provenance, finality and
        complete fee disclosure; the compatibility projection does not infer a
        fee from category or Router provenance.
      parameters:
        - $ref: "#/components/parameters/ChainIdQuery"
        - $ref: "#/components/parameters/CategoryQuery"
      responses:
        "200":
          description: Token-list compatibility document with explicit ready, degraded or unavailable quality.
          headers:
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
            ETag:
              $ref: "#/components/headers/ETag"
            X-Request-Id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TokenList"
        "304":
          $ref: "#/components/responses/NotModified"
        "400":
          $ref: "#/components/responses/BadRequest"
        "429":
          $ref: "#/components/responses/RateLimited"
        "503":
          $ref: "#/components/responses/Unavailable"

components:
  schemas:
    Status:
      $ref: ../schemas/v2/status.schema.json
    Manifest:
      $ref: ../schemas/v2/manifest.schema.json
    LaunchFeed:
      $ref: ../schemas/v2/launch-feed.schema.json
    Launch:
      $ref: ../schemas/v2/launch.schema.json
    LaunchPartnerAttributionV1:
      $ref: ../schemas/v2/launch-partner-attribution-v1.schema.json
    CanonicalCustomRegistryRecordV3:
      $ref: ../schemas/v2/custom-launch-registry-record-v3.schema.json
    CanonicalCustomRegistryRecordV4:
      $ref: ../schemas/v2/custom-launch-registry-record-v4.schema.json
    CustomFeeEnforcedLaunchProfileV2:
      $ref: ../schemas/v2/custom-fee-enforced-launch-profile-v2.schema.json
    DirectNativeHookGraphProfileDiscoveryV1:
      $ref: ../schemas/v2/direct-native-hook-graph-profile-discovery-v1.schema.json
    DirectNativeHookGraphProfileDiscoveryV2:
      $ref: ../schemas/v2/direct-native-hook-graph-profile-discovery-v2.schema.json
    DirectNativeHookGraphProfileDiscoveryV3:
      $ref: ../schemas/v2/direct-native-hook-graph-profile-discovery-v3.schema.json
    TokenList:
      $ref: ../schemas/v2/token-list.schema.json
    Problem:
      $ref: ../schemas/v2/problem.schema.json
    WellKnownDocument:
      type: object
      additionalProperties: false
      required:
        - schemaVersion
        - platformId
        - name
        - description
        - apiVersion
        - apiBaseUrl
        - statusUrl
        - manifestUrl
        - launchesUrl
        - tokenListUrl
        - openApiUrl
        - schemasBaseUrl
        - documentationUrl
        - sourceUrl
        - chains
        - publicCategories
        - compatibility
        - extensions
      properties:
        schemaVersion:
          const: 2.0.0
        platformId:
          const: programmable
        name:
          type: string
        description:
          type: string
        apiVersion:
          const: "2"
        apiBaseUrl:
          type: string
          format: uri
        statusUrl:
          type: string
          format: uri
        manifestUrl:
          type: string
          format: uri
        launchesUrl:
          type: string
          format: uri
        tokenListUrl:
          type: string
          format: uri
        openApiUrl:
          type: string
          format: uri
        schemasBaseUrl:
          type: string
          format: uri
        documentationUrl:
          type: string
          format: uri
        sourceUrl:
          type: string
          format: uri
        chains:
          type: array
          minItems: 1
          items:
            type: object
            additionalProperties: false
            required: [chainId, caip2, name, explorerUrl, status]
            properties:
              chainId:
                type: integer
                minimum: 1
              caip2:
                type: string
                pattern: ^eip155:[1-9][0-9]*$
              name:
                type: string
              explorerUrl:
                type: string
                format: uri
              status:
                const: live
        publicCategories:
          type: object
          additionalProperties: false
          required: [classic, custom]
          properties:
            classic:
              type: object
              additionalProperties: false
              required: [discoveryStatus]
              properties:
                discoveryStatus:
                  const: live
            custom:
              type: object
              additionalProperties: false
              required:
                - discoveryStatus
                - publicSubmissionStatus
                - customLaunchApiStatus
                - legacyRegistrySubmissionStatus
                - legacyGithubSubmissionStatus
                - registryAddress
                - registryStartBlock
                - registryGeneration
                - note
              properties:
                discoveryStatus:
                  const: live
                publicSubmissionStatus:
                  const: closed
                  description: Backward-compatible legacy Registry and GitHub submission intake status. V1 and V2 historical reads remain compatible, but authenticated POST returns nonretryable 409 CUSTOM_LAUNCH_V1_READ_ONLY or 409 CUSTOM_LAUNCH_V2_READ_ONLY. Only V3 profile 3.3.0 accepts fresh submissions.
                customLaunchApiStatus:
                  const: live
                  description: Retained V1/V2 historical read status plus active V3 profile 3.3.0 fresh-submission status. Resolve the current V3 create pointer from the custom-launch-api extension or v2 status.
                legacyRegistrySubmissionStatus:
                  const: closed
                  description: Legacy Custom Registry submission intake status.
                legacyGithubSubmissionStatus:
                  const: closed
                  description: Legacy GitHub submission intake status.
                registryAddress:
                  type: string
                  pattern: ^0x[0-9a-fA-F]{40}$
                registryStartBlock:
                  type: string
                  pattern: ^[1-9][0-9]*$
                registryGeneration:
                  type: string
                  pattern: ^[1-9][0-9]*$
                note:
                  type: string
        compatibility:
          type: object
          additionalProperties: false
          required:
            - majorVersion
            - additiveChangesOnly
            - unknownFields
            - unknownCapabilities
            - unknownMarketKinds
            - deploymentAddresses
          properties:
            majorVersion:
              const: 2
            additiveChangesOnly:
              const: true
            unknownFields:
              const: ignore
            unknownCapabilities:
              const: preserve
            unknownMarketKinds:
              const: display-as-unsupported
            deploymentAddresses:
              const: resolve-from-manifest
        extensions:
          type: object
          description: Namespaced, non-authoritative additive data. Consumers must never execute extension content.
          propertyNames:
            pattern: ^[a-z0-9]+(?:\.[a-z0-9-]+)+$
          additionalProperties:
            type: object

  parameters:
    ChainIdQuery:
      name: chainId
      in: query
      required: false
      description: Optional EIP-155 chain ID from `supportedChainIds`. Omit for the Ethereum compatibility lane.
      schema:
        type: integer
        minimum: 1
    CategoryQuery:
      name: category
      in: query
      required: false
      description: Stable public launch category.
      schema:
        type: string
        enum: [classic, custom]
    CursorQuery:
      name: cursor
      in: query
      required: false
      description: Opaque page-continuation cursor from `page.nextCursor`. Repeat the same `chainId` and `category` query scope that created it. Do not construct or parse it; do not combine it with `after`.
      schema:
        type: string
        minLength: 1
        maxLength: 1024
        pattern: "^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$"
    AfterQuery:
      name: after
      in: query
      required: false
      description: Opaque durable polling checkpoint from `page.resumeCursor`. Repeat the same `chainId` and `category` query scope that created it. Returns records observed after that checkpoint. Do not construct or parse it; do not combine it with `cursor`.
      schema:
        type: string
        minLength: 1
        maxLength: 1024
        pattern: "^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$"
    LimitQuery:
      name: limit
      in: query
      required: false
      description: Maximum records returned on one page.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 100
    ChainIdPath:
      name: chainId
      in: path
      required: true
      description: Published EIP-155 chain ID from `supportedChainIds`; planned chains remain explicitly non-authoritative.
      schema:
        type: integer
        minimum: 1
    TokenAddressPath:
      name: tokenAddress
      in: path
      required: true
      description: ERC-20 token contract address. Address comparison is case-insensitive on EVM chains.
      schema:
        type: string
        pattern: ^0x[a-fA-F0-9]{40}$
    LaunchIdPath:
      name: launchId
      in: path
      required: true
      description: Canonical opaque launch ID. Do not derive identity from a token name, symbol or logo.
      schema:
        type: string
        minLength: 1
        maxLength: 256
        pattern: ^[A-Za-z0-9][A-Za-z0-9._:@/+~-]{0,255}$

  headers:
    CacheControl:
      description: Public cache policy for this representation.
      schema:
        type: string
    ETag:
      description: Entity tag for conditional requests with If-None-Match.
      schema:
        type: string
    RequestId:
      description: Opaque request identifier for support and diagnostics.
      schema:
        type: string

  responses:
    NotModified:
      description: The representation has not changed since the supplied If-None-Match value.
      headers:
        ETag:
          $ref: "#/components/headers/ETag"
    BadRequest:
      description: The request parameters are invalid.
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    NotFound:
      description: The requested chain or launch identity is not published.
      headers:
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    RateLimited:
      description: The public rate limit has been exceeded.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
            minimum: 1
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    Unavailable:
      description: The service or required data source is temporarily unavailable.
      headers:
        Retry-After:
          description: Seconds to wait before retrying, when known.
          schema:
            type: integer
            minimum: 1
        X-Request-Id:
          $ref: "#/components/headers/RequestId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
