Skip to content
    OpenMoney
    LedgerMethodologySchemaSourcesDocs
    Sign inStart your Passport
    MethodologySchemaSourcesDocsTransparencyPrivacyTermsAccessibility

    © 2026 All Things AI, Inc.. Neutral by construction: no affiliate links, no paid placement, no earnings claims.

    Docs

    API reference

    Every route the real OpenMoney API serves, generated from the same Zod route schemas that run in production and rendered here live from the API's own GET /v1/openapi.json — a new or changed route appears the next time this page loads, with nothing hand-maintained in between.

    Version
    0.1.0
    Base URL
    https://api.openmoney.ai (production environment)
    Raw document
    /v1/openapi.json — same origin, same document this page renders
    Auth
    Session cookie for the signed-in app, or an org/agent API key (Authorization: Bearer om_test_… / om_live_…) for everything else. Every mutating route also requires an Idempotency-Key header.
    Errors
    RFC 9457 application/problem+json on every non-2xx response — type, title, status, detail, instance.

    1 route

    health

    Liveness and dependency checks.

    get/healthDependency health

    Runs real checks: `SELECT 1`, Redis `PING`, an S3 `HeadBucket`, and an SMTP handshake (results cached 30 s). Returns 503 when Postgres or Redis fail; storage/email problems only mark the service degraded.

    Auth: Public — no session or key required

    /health responses
    StatusContent typeDescription
    200application/jsonDefault Response
    429application/problem+jsonRate limited — retry after the `Retry-After` seconds.
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/jsonDefault Response

    1 route

    status

    Provider configuration status (sandbox/live/unconfigured).

    get/v1/status/providersProvider configuration status

    The single source of truth for sandbox/live/unconfigured, read by the admin Providers page and `SandboxBanner`.

    Auth: Public — no session or key required

    /v1/status/providers responses
    StatusContent typeDescription
    200application/jsonDefault Response
    429application/problem+jsonRate limited — retry after the `Retry-After` seconds.
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.

    10 routes

    registry

    Public registry measurements. Every figure is an EvidenceValue.

    get/v1/registry/coverageRegistry coverage counter

    EvidenceValue (unit `count`, source type `internal_measurement`). Phase 0 has no providers table yet, so the value is the number of distinct publishers with a source retrieved within 30 days, read live from the database; `n` is the total number of sources.

    Auth: Public — no session or key required

    /v1/registry/coverage responses
    StatusContent typeDescription
    200application/jsonDefault Response
    429application/problem+jsonRate limited — retry after the `Retry-After` seconds.
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/providersList providers

    Published providers, searchable and filterable. `q` uses trigram similarity plus a substring fallback for typo tolerance.

    Auth: Public — no session or key required

    /v1/providers parameters
    NameInTypeRequiredDescription

    7 routes

    auth

    Passkeys (WebAuthn), magic links, and sessions.

    post/v1/auth/passkey/register/optionsStart passkey registration

    Creates an unverified account for a new email, or adds a passkey to the signed-in account. An existing account that already has a passkey or a verified email must be signed in first (prevents takeover by registering a credential on someone else's address). Exempt from `Idempotency-Key`: the WebAuthn challenge is single-use by construction.

    Auth: Public — no session or key required

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    emailstring (email) (0–254 chars)required—
    displayNamestring (1–80 chars)optional—
    /v1/auth/passkey/register/options responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response

    8 routes

    me

    The signed-in user: profile, devices, passkeys, consent ledger.

    get/v1/meCurrent identity

    Works with the session cookie or a Bearer API key (the key's owning user and org are returned).

    Auth: Session cookie or API key

    /v1/me responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    patch/v1/meUpdate profile

    Auth: Session cookie

    /v1/me parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    14 routes

    orgs

    Organizations, membership, and scoped API keys.

    get/v1/orgsOrganizations you belong to

    Auth: Session cookie

    /v1/orgs responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/orgsCreate an organization

    Creates the org, makes the caller its owner, and activates it on the current session. Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/orgs parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    20 routes

    admin

    Operator routes: providers, flags, jobs, sources, audit.

    get/v1/admin/providersProvider statuses and the last health check

    Auth: Session cookie

    /v1/admin/providers responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/admin/flagsFeature flags

    Auth: Session cookie

    /v1/admin/flags responses
    StatusContent typeDescription
    200application/jsonDefault Response

    1 route

    sources

    Public list of evidence sources with reachability.

    get/v1/sourcesPublic evidence sources

    Every source the registry cites, with its canonical URL (no affiliate or tracking parameters), publisher, type, reachability, and last check time.

    Auth: Public — no session or key required

    /v1/sources responses
    StatusContent typeDescription
    200application/jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.

    4 routes

    agents

    Agent principals, spend policies, bounties, and OAuth 2.1 client credentials (PRD §5).

    post/v1/oauth/tokenOAuth 2.1 client-credentials grant for agents

    Exchanges an agent's real API key id/secret (from POST /v1/agents) for a short-lived (1 hour) signed bearer JWT, for apps/mcp's streamable-HTTP transport specifically. Real jose HS256 signing — an unknown client_id, a wrong client_secret, or a revoked/expired key all fail with a real RFC 6749 `invalid_client` error, never a fabricated token.

    Auth: Public — no session or key required

    /v1/oauth/token parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    grant_typestringrequiredRFC 6749 §4.4 — only the client-credentials grant is supported.
    client_idstring (1–200 chars)requiredThe agent's apiKeys row id, or its prefix — either identifies the same key.
    client_secret

    13 routes

    bounties

    Funded, Proof-gated task bounties: post, apply, accept, submit, approve, dispute (PRD §5.5).

    get/v1/bountiesPublic list of open bounties

    Auth: Public — no session or key required

    /v1/bounties parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    countryquerystring (2–2 chars)optional—
    minPriceAmountMinorqueryinteger (0–9007199254740991)optional—
    maxPriceAmountMinorqueryinteger (0–9007199254740991)optional—
    /v1/bounties responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+json

    1 route

    ledger

    Real Stripe/x402 money movements (PRD §5.6). Never a computed balance.

    get/v1/ledgerReal money movements the caller is a party to

    Every `ledger_entries` row where the caller is the bounty's poster (directly, or via an org they belong to) or its accepted earner, newest first. Each row carries the real Stripe/x402 reference and counterparty that actually moved the money — never a computed balance. Works with the session cookie or a Bearer API key.

    Auth: Session cookie or API key

    /v1/ledger parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    /v1/ledger responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.

    4 routes

    rankings

    Open Opportunity Rankings: quarterly reports, movers, CSV/PDF downloads (PRD §6.2).

    get/v1/rankingsPublished rankings report periods

    Every real, published `rankings_reports` period, newest first — backs the report index and RSS/changelog-style discovery.

    Auth: Public — no session or key required

    /v1/rankings responses
    StatusContent typeDescription
    200application/jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/rankings/{period}One published rankings report

    The real, immutable snapshot for this period — `@openmoney/rankings`'s own `RankingsReport` plus its real `Mover[]` and methodology version. 404 when the period has not been published yet (a real absence, not "insufficient evidence").

    Auth: Public — no session or key required

    /v1/rankings/{period} parameters
    NameInTypeRequiredDescription

    2 routes

    datasets

    Downloadable, versioned registry dataset snapshots — CSV and Parquet (PRD §6.3).

    get/v1/datasetsAvailable dataset snapshots

    Every real, published `dataset_snapshots` row (registry CSV and Parquet exports), newest first.

    Auth: Public — no session or key required

    /v1/datasets responses
    StatusContent typeDescription
    200application/jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/datasets/{id}/downloadDownload a dataset snapshot (redirect)

    Redirects (303) to the real file in storage for this dataset snapshot — signed URL redirect, same pattern `documents` downloads already use. 404 if the id does not exist; 503 if storage is not configured.

    Auth: Public — no session or key required

    /v1/datasets/{id}/download parameters
    NameInTypeRequiredDescription
    id

    3 routes

    applications

    post/v1/applications/draftDraft a proposal/cover note from evidenced facts only

    Rate-capped at 10 drafts per rolling 24h (429 with Retry-After when exceeded). Rejects a draft that doesn't reference the opportunity's title/kind. No submit endpoint exists anywhere in this API — the user copies or opens the canonical application page themselves. Idempotency-Key required.

    Auth: Session cookie

    /v1/applications/draft parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    opportunityIdstring (1–200 chars)required—
    includeFactKeysstring (1–120 chars)[]optional—
    /v1/applications/draft responses
    StatusContent typeDescription
    201

    6 routes

    billing

    get/v1/orgs/{id}/usageReal usage for one org's current (or requested) billing period

    Session (a member of the org) or the org's own agent key. Defaults to the current UTC calendar month. When Stripe is configured and the org's active API-key tier has a real, currently-active metered Price, every money field is populated from it; otherwise every money field is honestly `null` — real usage, no invented cost.

    Auth: Session cookie or API key

    /v1/orgs/{id}/usage parameters
    NameInTypeRequiredDescription
    periodStartquerystring (date-time)optional—
    periodEndquerystring (date-time)optional—
    idpathstring (uuid)required—
    /v1/orgs/{id}/usage responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+json

    7 routes

    discover

    get/v1/discoverRanked, explainable opportunity feed

    Runs the caller's profile through `@openmoney/matching`'s hard filters, expected-value scoring, and diversity constraint. `showExcluded=true` includes hard-filtered items (`passesHardFilters: false`) with real reasons instead of dropping them.

    Auth: Session cookie

    /v1/discover parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    sortquery"expected_value" | "net_hourly" | "payout_speed" | "freshness"optional—
    showExcludedquerybooleanoptional—
    workModequery"remote" | "onsite" | "hybrid"optionalWhere the work happens.
    countryquerystring (2–2 chars)optional—
    region

    4 routes

    discovery

    post/v1/link-submissionsQueue a link for a curator to review — no automatic fetch or analysis

    Session auth (so there is a real person to notify once it is reviewed). Creates a real `link_submissions` row, `status: "queued"`; nothing is fetched or analyzed at submission time. Idempotency-Key required.

    Auth: Session cookie

    /v1/link-submissions parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    urlstring (uri) (0–2000 chars)required—
    notestring (0–500 chars)optional—
    /v1/link-submissions responses
    StatusContent typeDescription
    201application/jsonDefault Response

    7 routes

    moderation

    get/v1/admin/moderation/queueModeration queue: real reports, oldest first, with real fraud-signal context

    Real `reports` rows, oldest-`createdAt`-first (mirrors `GET /v1/admin/audit`'s keyset-pagination convention, direction flipped — a moderator working the default, unfiltered view sees the oldest report first; combined with `?status=open` this literally is "oldest-open-first"). Each item carries its real `moderationDecisions` row as `decision` (null until `resolved` — a report has at most one, since `resolved`/`dismissed` are both terminal) and real, non-blocking fraud-signal context from `@openmoney/trust-safety` (`fraud-signals.ts`): the reporter's own report-submission velocity, any real duplicate-account cluster their recent `device_signals` participate in, and — only for `subjectType: "bounty"` reports — the real bounty poster's own posting velocity. None of this ever blocks or restricts anything by itself.

    Auth: Session cookie

    /v1/admin/moderation/queue parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    statusquery"open" | "queued" | "resolved" | "dismissed"optional—
    subjectTypequery"provider" | "opportunity" | "bounty" | "message" | "proof_presentation"optional

    27 routes

    passport

    get/v1/passport/sourcesList the caller's income sources

    Auth: Session cookie

    /v1/passport/sources responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/passport/sourcesCreate a manual/CSV/PDF/screenshot income source

    Idempotency-Key required. Gmail/Argyle/Pinwheel sources are created by their own connect/callback/webhook routes, not this one.

    Auth: Session cookie

    /v1/passport/sources parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    10 routes

    profile

    get/v1/profileGet the caller's profile and constraints

    Never 404s: a caller with no `profiles` row yet gets PRD-documented defaults (capital $0, risk tolerance conservative, payout speed "any", empty everything else).

    Auth: Session cookie

    /v1/profile responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    patch/v1/profileUpdate the profile

    Partial update, PRD §4.2's field list. Creates the `profiles` row on first write. Idempotency-Key required.

    Auth: Session cookie

    /v1/profile parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)

    21 routes

    proof

    get/v1/orgs/{orgId}/proof-policiesList proof policies

    Auth: Session cookie

    /v1/orgs/{orgId}/proof-policies parameters
    NameInTypeRequiredDescription
    orgIdpathstring (uuid)required—
    /v1/orgs/{orgId}/proof-policies responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/orgs/{orgId}/proof-policiesCreate a proof policy ("what we require")

    1 route

    reports

    post/v1/reportsFile a report against a real provider, opportunity, bounty, message, or Proof presentation

    Verifies the real subject exists first for `provider`/`opportunity`/`bounty` subjectTypes (real 404 if not — never a report against nothing); `message`/`proof_presentation` have no dedicated table in this schema yet, so those are accepted on the caller's word (see `services/moderation.ts`). Creates a real `reports` row, `status: 'open'`. Idempotency-Key required.

    Auth: Session cookie

    /v1/reports parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    subjectType"provider" | "opportunity" | "bounty" | "message" | "proof_presentation"required—
    subjectIdstring (1–200 chars)required—
    categorystring (1–100 chars)required

    1 route

    transparency

    get/v1/transparencyPublished monthly transparency counts

    Every real, published `transparency_counts_snapshots` row, newest period first — real counts only, no live-computed numbers mixed in with historical ones (`apps/worker/src/jobs/transparency-counts.ts` is the only writer).

    Auth: Public — no session or key required

    /v1/transparency responses
    StatusContent typeDescription
    200application/jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.

    7 routes

    watches

    get/v1/watchesList the caller's watches

    Auth: Session cookie

    /v1/watches responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/watchesWatch an opportunity, provider, or saved search

    Exactly one of `opportunityId`/`providerId`/`searchQuery` must be present, matching `subjectType`. Idempotency-Key required.

    Auth: Session cookie

    /v1/watches parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    qquerystring (1–200 chars)optional—
    categoryquery"rideshare" | "delivery" | "local_shifts" | "home_services" | "care" | "freelance" | "ai_training" | "microtask" | "research" | "selling" | "leasing" | "tutoring" | "creator" | "agent_bounty" | "open_source" | "other"optionalRegistry category.
    kindquery"platform_gig" | "freelance_marketplace" | "ai_training" | "local_shift" | "selling" | "leasing" | "research_study" | "agent_bounty" | "employer_posting" | "open_source_bounty"optionalWhat kind of earning this is.
    countryquerystring (2–2 chars)optional—
    statusquery"published"optional—
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    sortquery"composite_desc" | "composite_asc" | "updated_desc"optional—
    /v1/providers responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/providers/{id}Get a provider

    Full provider record, shaped to round-trip through `@openmoney/schema`'s `Provider` type. 404 when unpublished or missing.

    Auth: Public — no session or key required

    /v1/providers/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    /v1/providers/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/providers/{id}/scorecardGet a provider's scorecard

    Latest scorecard for the provider's primary opportunity (the earliest-published one), or for `?opportunityId=` when given.

    Auth: Public — no session or key required

    /v1/providers/{id}/scorecard parameters
    NameInTypeRequiredDescription
    opportunityIdquerystring (1–200 chars)optional—
    idpathstring (1–200 chars)required—
    /v1/providers/{id}/scorecard responses
    StatusContent typeDescription
    200application/jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/opportunitiesList / search opportunities

    Published opportunities, searchable and filterable. `q` uses trigram similarity over `title` (typo-tolerant) plus a substring match over `description`. Public and unauthenticated by default; also accepts a real `X-PAYMENT` header (x402, PRD §5.3) as an alternative, keyless access path for agents — honestly 402 while x402 is unconfigured in this environment, never a fabricated payment acceptance.

    Auth: Public — no session or key required

    /v1/opportunities parameters
    NameInTypeRequiredDescription
    qquerystring (1–200 chars)optional—
    kindquery"platform_gig" | "freelance_marketplace" | "ai_training" | "local_shift" | "selling" | "leasing" | "research_study" | "agent_bounty" | "employer_posting" | "open_source_bounty"optionalWhat kind of earning this is.
    providerIdquerystring (1–200 chars)optional—
    countryquerystring (2–2 chars)optional—
    workModequery"remote" | "onsite" | "hybrid"optionalWhere the work happens.
    minCompositequeryinteger (0–100)optional—
    maxFeesBpsqueryinteger (0–9007199254740991)optional—
    statusquery"published"optional—
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    sortquery"composite_desc" | "composite_asc" | "updated_desc"optional—
    /v1/opportunities responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    402application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/opportunities/{id}Get an opportunity

    Full opportunity record, shaped to round-trip through `@openmoney/schema`'s `Opportunity` type. 404 when unpublished or missing. Public and cross-origin: `Access-Control-Allow-Origin: *` (real e2e embedding needs this — see `packages/scorecard-embed`).

    Auth: Public — no session or key required

    /v1/opportunities/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    /v1/opportunities/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/opportunities/{id}/historyOpportunity change history

    What changed and when: merged `record_versions` (content edits) and `scorecard_history` (recomputes), newest first.

    Auth: Public — no session or key required

    /v1/opportunities/{id}/history parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    /v1/opportunities/{id}/history responses
    StatusContent typeDescription
    200application/jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/methodology/registryScoring methodology

    Generated directly from `@openmoney/scoring`'s `METHODOLOGY` constant: weights, per-dimension formula text, and the changelog. No database access.

    Auth: Public — no session or key required

    /v1/methodology/registry responses
    StatusContent typeDescription
    200application/jsonDefault Response
    429application/problem+jsonRate limited — retry after the `Retry-After` seconds.
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/changelogRecord version history across providers and opportunities

    Append-only history from `record_versions`, newest first. Filter with `subjectType` and/or `subjectId`.

    Auth: Public — no session or key required

    /v1/changelog parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    subjectTypequery"provider" | "opportunity"optional—
    subjectIdquerystring (1–200 chars)optional—
    /v1/changelog responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/listings/verifyCheck a URL or raw text for real scam signals, with evidence

    Fetches the URL live (robots-respecting) or scans the supplied text, and runs every real scam-signature rule (`@openmoney/scoring`). A flag never exists without a matched excerpt/lookup — no flags does not mean "safe", it means the current rule set found nothing. Also accepts a real `X-PAYMENT` header (x402) as an alternative, keyless access path — honestly 402 while x402 is unconfigured in this environment.

    Auth: Public — no session or key required

    /v1/listings/verify parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    urlstring (uri)optional—
    textstring (1–20000 chars)optional—
    /v1/listings/verify responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    402application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/auth/passkey/register/verifyFinish passkey registration

    Verifies the attestation against the stored challenge (5-minute expiry, single use), stores the credential, and starts a session when none exists. Exempt from `Idempotency-Key`: the WebAuthn challenge is single-use by construction.

    Auth: Public — no session or key required

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    emailstring (email) (0–254 chars)required—
    responseRegistrationResponseInputrequired—
    nicknamestring (1–60 chars)optional—
    /v1/auth/passkey/register/verify responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/auth/passkey/login/optionsStart passkey sign-in

    With an email, lists that account's credentials in `allowCredentials`; without one (or for an unknown/passkey-less email) returns discoverable-credential options, so the response never reveals whether an email exists. Exempt from `Idempotency-Key`: the WebAuthn challenge is single-use by construction.

    Auth: Public — no session or key required

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    emailstring (email) (0–254 chars)optional—
    /v1/auth/passkey/login/options responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/auth/passkey/login/verifyFinish passkey sign-in

    Verifies the assertion, updates the signature counter, and sets the session cookie. Exempt from `Idempotency-Key`: the WebAuthn challenge is single-use by construction.

    Auth: Public — no session or key required

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    responseAuthenticationResponseInputrequired—
    /v1/auth/passkey/login/verify responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/auth/magic-linkEmail a sign-in link

    Always responds 202 without revealing whether the address has an account. `provider` is `sandbox` (Mailpit), `live` (SMTP), or `unconfigured` (nothing was sent; the UI shows how to connect email). The link is single-use and expires in 15 minutes. Requires `Idempotency-Key`.

    Auth: Public — no session or key required

    /v1/auth/magic-link parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    emailstring (email) (0–254 chars)required—
    redirectTostring (0–512 chars)optional—
    /v1/auth/magic-link responses
    StatusContent typeDescription
    202application/jsonDefault Response
    400application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/auth/magic-link/consumeConsume a sign-in link

    Marks the link used, verifies the email, starts a session, and redirects (303) to `APP_WEB_ORIGIN + redirectTo` (default `/app`). Invalid or reused links redirect to `/sign-in?error=magic-link-invalid`. GET routes are never subject to `Idempotency-Key`.

    Auth: Public — no session or key required

    /v1/auth/magic-link/consume parameters
    NameInTypeRequiredDescription
    tokenquerystring (16–256 chars)required—
    /v1/auth/magic-link/consume responses
    StatusContent typeDescription
    303—Redirect to the web app
    400application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/auth/logoutSign out

    Revokes the current session and clears the cookie. Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/auth/logout parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/auth/logout responses
    StatusContent typeDescription
    204—Default Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    displayNamestring (1–80 chars)optional—
    handlestringoptional—
    localestringoptional—
    timezonestringoptional—
    /v1/me responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/me/sessionsDevice list

    Auth: Session cookie

    /v1/me/sessions responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    delete/v1/me/sessions/{id}Sign out a device

    Auth: Session cookie

    /v1/me/sessions/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/me/sessions/{id} responses
    StatusContent typeDescription
    204—Default Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/me/passkeysPasskeys on this account

    Auth: Session cookie

    /v1/me/passkeys responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    patch/v1/me/passkeys/{id}Rename a passkey

    Auth: Session cookie

    /v1/me/passkeys/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    nicknamestring (1–60 chars)required—
    /v1/me/passkeys/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    delete/v1/me/passkeys/{id}Delete a passkey

    Refused (409 `last-credential`) when it is the only passkey and the email is not verified, because the account would become unreachable.

    Auth: Session cookie

    /v1/me/passkeys/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/me/passkeys/{id} responses
    StatusContent typeDescription
    204—Default Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/me/consentConsent ledger

    Every data access, import, share, verification, and export, newest first. Pass `cursor` from the previous page.

    Auth: Session cookie

    /v1/me/consent parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    /v1/me/consent responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    namestring (2–80 chars)required—
    kind"relying_party" | "agent_operator" | "employer"optional—
    websitestring (uri)optional—
    /v1/orgs responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/orgs/{id}/activateSwitch the active organization

    Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/orgs/{id}/activate parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/orgs/{id}/activate responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/orgs/{id}/request-verificationRequest business verification

    Moves the organization from `unverified` to `pending` manual review. Stripe Identity's business-verification flow is not configured yet (no `STRIPE_SECRET_KEY`), so review is done by a human, not issued instantly — this route never sets `verified` itself. Idempotent: calling again while already `pending`/`verified`/`rejected` just returns the current state. Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/orgs/{id}/request-verification parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/orgs/{id}/request-verification responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/orgs/{id}/api-keysAPI keys for an organization

    Auth: Session cookie

    /v1/orgs/{id}/api-keys parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    /v1/orgs/{id}/api-keys responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/orgs/{id}/api-keysCreate an API key

    The full key is returned once and stored only as a SHA-256 hash. Prefix is `om_live_` in production and `om_test_` elsewhere. Writes a `share:api_key` consent event for the creator. Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/orgs/{id}/api-keys parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    namestring (1–80 chars)required—
    scopes"me:read" | "registry:read" | "sources:read" | "proof:verify" | "proof:request" | "bounties:write" | "agents:write"[]required—
    /v1/orgs/{id}/api-keys responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    delete/v1/orgs/{id}/api-keys/{keyId}Revoke an API key

    Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/orgs/{id}/api-keys/{keyId} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    keyIdpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/orgs/{id}/api-keys/{keyId} responses
    StatusContent typeDescription
    204—Default Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/orgs/{id}/auditAudit log for an organization

    The organization's own audit trail (org, API key, webhook, policy, and Proof-request changes) — owner/admin only, keyset-paginated newest first. Never includes `ipHash`.

    Auth: Session cookie

    /v1/orgs/{id}/audit parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    actionquerystring (0–100 chars)optional—
    idpathstring (uuid)required—
    /v1/orgs/{id}/audit responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/orgs/{orgId}/proof-policiesList proof policies

    Auth: Session cookie

    /v1/orgs/{orgId}/proof-policies parameters
    NameInTypeRequiredDescription
    orgIdpathstring (uuid)required—
    /v1/orgs/{orgId}/proof-policies responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/orgs/{orgId}/proof-policiesCreate a proof policy ("what we require")

    Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/orgs/{orgId}/proof-policies parameters
    NameInTypeRequiredDescription
    orgIdpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    namestring (1–120 chars)required—
    requirementsobject[]required—
    /v1/orgs/{orgId}/proof-policies responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/orgs/{orgId}/webhooksList webhook endpoints (never returns signingSecret again)

    Auth: Session cookie

    /v1/orgs/{orgId}/webhooks parameters
    NameInTypeRequiredDescription
    orgIdpathstring (uuid)required—
    /v1/orgs/{orgId}/webhooks responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/orgs/{orgId}/webhooksCreate a webhook endpoint

    Generates and returns `signingSecret` ONCE — never retrievable again, same convention as an API key's raw value. Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/orgs/{orgId}/webhooks parameters
    NameInTypeRequiredDescription
    orgIdpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    urlstring (uri)required—
    events"proof.verified" | "proof.revoked" | "proof.expired"[]required—
    /v1/orgs/{orgId}/webhooks responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/orgs/{orgId}/webhooks/{id}/deliveriesRecent deliveries for one webhook endpoint (audit/debugging)

    Auth: Session cookie

    /v1/orgs/{orgId}/webhooks/{id}/deliveries parameters
    NameInTypeRequiredDescription
    limitqueryinteger (1–200)optional—
    orgIdpathstring (uuid)required—
    idpathstring (uuid)required—
    /v1/orgs/{orgId}/webhooks/{id}/deliveries responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/orgs/{orgId}/proof-verificationsThe org's own proof_verification_events (usage dashboard)

    Auth: Session cookie

    /v1/orgs/{orgId}/proof-verifications parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    orgIdpathstring (uuid)required—
    /v1/orgs/{orgId}/proof-verifications responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    401
    application/problem+json
    Default Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    patch/v1/admin/flags/{key}Update a feature flag

    Requires `Idempotency-Key`. Writes before/after to the audit log.

    Auth: Session cookie

    /v1/admin/flags/{key} parameters
    NameInTypeRequiredDescription
    keypathstring (1–100 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    enabledbooleanoptional—
    rolloutPercentinteger (0–100)optional—
    audienceobjectoptional—
    descriptionstring (1–500 chars)optional—
    /v1/admin/flags/{key} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/admin/jobsQueues and recent job runs

    Live BullMQ counts per queue plus the last 50 `jobs_log` rows written by the worker.

    Auth: Session cookie

    /v1/admin/jobs responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/jobs/link-checkEnqueue a link-check run

    Adds a `link-check.run` job (payload validated by `linkCheckJobSchema`) with id `link-check-<uuid>` (BullMQ forbids `:` in custom ids). Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/admin/jobs/link-check parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    sourceIdsstring (uuid)[]optional—
    limitinteger (−∞–5000)optional—
    /v1/admin/jobs/link-check responses
    StatusContent typeDescription
    202application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/jobs/rankings-reportEnqueue a rankings-report run

    Adds a `rankings-report.run` job (PRD §6.2) with id `rankings-report-<uuid>`, for demoing the quarterly report without waiting a quarter. `period` (e.g. "2026-q3") defaults to the real current quarter; the job is a real no-op if that period is already published. Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/admin/jobs/rankings-report parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    periodstringoptional—
    /v1/admin/jobs/rankings-report responses
    StatusContent typeDescription
    202application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/jobs/dataset-snapshotEnqueue a dataset-snapshot run

    Adds a `dataset-snapshot.run` job (PRD §6.3) with id `dataset-snapshot-<uuid>`, for demoing without waiting for the real weekly schedule. No parameters — always exports the currently-published registry. Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/admin/jobs/dataset-snapshot parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/admin/jobs/dataset-snapshot responses
    StatusContent typeDescription
    202application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/jobs/data-retentionEnqueue a data-retention sweep run

    Adds a `data-retention.run` job (PRD §7.3) with id `data-retention-<uuid>`, for demoing the nightly privacy-retention sweep without waiting for the real schedule. No parameters — every documented table's own real retention cutoff (`apps/worker/src/jobs/data-retention.ts`) is evaluated against the real clock each run. Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/admin/jobs/data-retention parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/admin/jobs/data-retention responses
    StatusContent typeDescription
    202application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/jobs/transparency-countsEnqueue a transparency-counts run

    Adds a `transparency-counts.run` job (PRD §7.2) with id `transparency-counts-<uuid>`, for demoing the monthly transparency-counts publication without waiting for the real schedule. `period` (e.g. "2026-08") defaults to the real current month; the job is a real no-op if that period is already published. Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/admin/jobs/transparency-counts parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    periodstringoptional—
    /v1/admin/jobs/transparency-counts responses
    StatusContent typeDescription
    202application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/admin/jobs/{id}One job: queue state plus its log rows

    Auth: Session cookie

    /v1/admin/jobs/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    /v1/admin/jobs/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/admin/sourcesSources with reachability and last check

    Auth: Session cookie

    /v1/admin/sources parameters
    NameInTypeRequiredDescription
    statusquery"unchecked" | "ok" | "redirected" | "unreachable" | "changed"optional—
    /v1/admin/sources responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/admin/auditAudit log

    Auth: Session cookie

    /v1/admin/audit parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    actionquerystring (0–100 chars)optional—
    actorUserIdquerystring (uuid)optional—
    /v1/admin/audit responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/registry/providersCreate a draft provider, optionally fetching its known policy pages

    Runs the curator fetch step against any `urls` supplied and writes the provider/opportunity rows via the same writer the pipeline and confirm route use (with no facts yet, since nothing has been extracted). Idempotency-Key required.

    Auth: Session cookie

    /v1/admin/registry/providers parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    domainstring (3–255 chars)requiredCanonical registrable domain, e.g. "example.com".
    brandNamestring (1–200 chars)required—
    legalNamestring (1–200 chars)optionalDefaults to `brandName` when omitted; a curator confirms the real legal name once the terms page is extracted.
    otherDomainsstring (1–∞ chars)[]optional—
    hqobject | nulloptional—
    jurisdictionsstring (1–∞ chars)[]optional—
    kinds"platform_gig" | "freelance_marketplace" | "ai_training" | "local_shift" | "selling" | "leasing" | "research_study" | "agent_bounty" | "employer_posting" | "open_source_bounty"[]optional—
    categoriesstring (1–∞ chars)[]optional—
    urlsobjectoptionalKnown policy-page URLs (`docs/registry/provider-sources.json` shape). Every URL supplied here is fetched immediately.
    /v1/admin/registry/providers responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/registry/providers/{id}/fetchRe-fetch a provider's known URLs, snapshot each, and update sources

    Uses the cached curator session for this provider when present (from `POST /providers` or a prior `/fetch`); otherwise reconstructs known URLs from `termsUrl`/`payoutPolicyUrl`/`deactivationPolicyUrl` on the provider row. Pass `urls` in the body to add or override slots (needed for `fees`/`payDisclosure` once the cache has expired, since those are not stored as columns). Idempotency-Key required.

    Auth: Session cookie

    /v1/admin/registry/providers/{id}/fetch parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    urlsobjectoptional—
    /v1/admin/registry/providers/{id}/fetch responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/registry/providers/{id}/extractRun LLM structured extraction over the freshest fetched pages

    Proposes field values with `confidence` and `corroborated` (ADR-007: a value earns `high`/`corroborated: true` only by appearing, normalized, in the real fetched page text) — nothing is written yet. Requires `POST .../fetch` to have run first in this session. Idempotency-Key required.

    Auth: Session cookie

    /v1/admin/registry/providers/{id}/extract parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/admin/registry/providers/{id}/extract responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/registry/providers/{id}/confirmAccept or reject proposed fields; write the accepted ones

    Body `{ fields: [{ path, accept }] }`. Writes accepted fields via the same writer the automated pipeline uses; rejected fields are dropped. Requires `POST .../extract` to have run first in this session. Idempotency-Key required.

    Auth: Session cookie

    /v1/admin/registry/providers/{id}/confirm parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    fieldsobject[]required—
    /v1/admin/registry/providers/{id}/confirm responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/registry/providers/{id}/publishPublish a provider once it clears the evidence bar

    Refuses (409) unless the provider has at least 3 distinct sources and one corroborated fee or payout fact. On success, bumps `version`, snapshots `record_versions`, and recomputes every opportunity's scorecard. Idempotency-Key required.

    Auth: Session cookie

    /v1/admin/registry/providers/{id}/publish parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/admin/registry/providers/{id}/publish responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/registry/opportunities/{id}/recomputeRecompute one opportunity's scorecard from current DB facts

    Auth: Session cookie

    /v1/admin/registry/opportunities/{id}/recompute parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/admin/registry/opportunities/{id}/recompute responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/admin/registry/queueFreshness and confirmation queue

    Every opportunity, flagged `stale` (`lastVerifiedAt` null or older than 30 days) and `needsConfirmation` (provider not yet `published` — the closest DB-backed proxy for "has fields a curator has not confirmed"), stale-and-unconfirmed first.

    Auth: Session cookie

    /v1/admin/registry/queue responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/admin/registry/providers/{id}Full provider record for curator review, including pending proposed fields

    Auth: Session cookie

    /v1/admin/registry/providers/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    /v1/admin/registry/providers/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    string (1–500 chars)
    required
    The raw API key the caller received once from POST /v1/agents.
    /v1/oauth/token responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonRate limited — retry after the `Retry-After` seconds.
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/agentsRegister an agent under the caller (or their active agent_operator org)

    Principal is the caller's session, or their active organization when it is an `agent_operator` org (same 'active org' convention as `POST /v1/orgs/{id}/api-keys`) — never any other org kind. `id` is the supplied `didKey`, or a generated `agent_<uuid>`. Creates a real `apiKeys` row (scopes `agents:write`, `bounties:write`, `proof:verify`) and returns the raw key **once** — store it now, it is never retrievable again. Idempotency-Key required.

    Auth: Session cookie

    /v1/agents parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    namestring (1–120 chars)required—
    didKeystring (1–500 chars)optionalA did:key the caller generated client-side. Omit for a platform-issued id (agent_<uuid>).
    policyAgentPolicyBodyInputrequired—
    /v1/agents responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/agents/{id}/policyThe agent's own spend policy

    Readable by the agent's own API key, or by its principal (session).

    Auth: Session cookie or API key

    /v1/agents/{id}/policy parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    /v1/agents/{id}/policy responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    patch/v1/agents/{id}/policyUpdate the agent's spend policy

    Principal-only (owner/admin of the principal org, or the principal user) — never the agent's own key, so an agent can never raise its own spending limit. Idempotency-Key required.

    Auth: Session cookie

    /v1/agents/{id}/policy parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    maxPerBountyAmountMinorinteger (−∞–9007199254740991)optional—
    maxPerBountyCurrencystringoptionalISO 4217 currency code, e.g. USD, INR.
    maxPerDayAmountMinorinteger (−∞–9007199254740991)optional—
    maxPerDayCurrencystringoptionalISO 4217 currency code, e.g. USD, INR.
    allowedKinds"platform_gig" | "freelance_marketplace" | "ai_training" | "local_shift" | "selling" | "leasing" | "research_study" | "agent_bounty" | "employer_posting" | "open_source_bounty"[]optional—
    allowedCountriesstring (2–2 chars)[]optional—
    /v1/agents/{id}/policy responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    Default Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/bountiesPost a bounty (real content-rule check; blocked bounties are recorded, not silently dropped)

    Caller is a signed-in principal (web) or an agent API key (`bounties:write` scope). An agent-posted bounty additionally requires a body `mandate`, verified via a real ES256 JWS check and checked against the real trailing-24h spend policy — a failed mandate or policy check 403s with the real reason, no row written. On success, `checkBountyContent` always runs; any real violation 403s with the rule code(s) cited and the bounty is still created (`status: 'cancelled'`) with the result attached for audit. A clean bounty is created `pending_funding`, ready to fund. Idempotency-Key required.

    Auth: Session cookie or API key

    /v1/bounties parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    titlestring (4–200 chars)required—
    taskSpecstring (10–10000 chars)required—
    deliverableSpecstring (4–2000 chars)required—
    remotebooleanoptional—
    geoCountriesstring (2–2 chars)[]optional—
    geoCitiesstring (1–120 chars)[]optional—
    deadlinestring (date-time)optional—
    priceAmountMinorinteger (−∞–9007199254740991)required—
    priceCurrencystringrequiredISO 4217 currency code, e.g. USD, INR.
    requiredProofPolicyobject[]optional—
    maxApplicantsinteger (−∞–500)optional—
    mandateMandateBodyInputoptional—
    /v1/bounties responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/bounties/{id}/fundFund a bounty (pending_funding -> funded -> open)

    Real Stripe PaymentIntent (manual capture, captured immediately) or an x402 funding intent, per `rail`. Honestly blocked (503 `stripe-unconfigured`/`x402-unconfigured`) while credentials are unset — the one real funding boundary this environment cannot cross. On real success: a real `ledger_entries` row (`kind: 'fund'`), then `funded -> open` in the same call. Poster/principal only. Idempotency-Key required.

    Auth: Session cookie or API key

    /v1/bounties/{id}/fund parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    rail"stripe" | "x402"optional—
    /v1/bounties/{id}/fund responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    get/v1/bounties/{id}Bounty detail — public for open/accepted/submitted, full for the poster/principal

    Auth: Public — no session or key required

    /v1/bounties/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    /v1/bounties/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/bounties/{id}/applicantsApplicants for one bounty — poster/principal only

    Auth: Session cookie or API key

    /v1/bounties/{id}/applicants parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    /v1/bounties/{id}/applicants responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/bounties/{id}/applyApply with real Proof shares satisfying requiredProofPolicy

    Verifies each real `proof_shares` row (ownership, non-revoked, matching type/freshness) against `requiredProofPolicy` before writing. `maxApplicants` enforced. Idempotency-Key required.

    Auth: Session cookie

    /v1/bounties/{id}/apply parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    proofShareIdsstring (uuid)[]optional—
    /v1/bounties/{id}/apply responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/bounties/{id}/acceptAccept one applicant, reject the rest (open -> accepted). Poster/principal only.

    Auth: Session cookie or API key

    /v1/bounties/{id}/accept parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    applicationIdstring (uuid)required—
    /v1/bounties/{id}/accept responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/bounties/{id}/submissionsSubmit deliverables (accepted -> submitted). The accepted earner only.

    Auth: Session cookie

    /v1/bounties/{id}/submissions parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    deliverablesobject[]required—
    /v1/bounties/{id}/submissions responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/bounties/{id}/submissions/{sid}/approveApprove a submission (submitted -> approved), then release funds (approved -> released)

    Poster/principal only. Marks the deliverable approved for real, then attempts the real release (Stripe capture+transfer, or x402) — honestly blocked (503) while unconfigured, in which case the bounty is left `approved` (accepted, payout pending real credentials), never silently stuck at `submitted`. On real success: two `ledger_entries` rows (`release` to the earner, `fee` to the platform). Idempotency-Key required.

    Auth: Session cookie or API key

    /v1/bounties/{id}/submissions/{sid}/approve parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    sidpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/bounties/{id}/submissions/{sid}/approve responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    post/v1/bounties/{id}/disputesOpen a dispute (submitted -> disputed). Either side — a human principal via session, or an agent (whose API key resolves to its principal) via Bearer.

    Auth: Session cookie or API key

    /v1/bounties/{id}/disputes parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    submissionIdstring (uuid)required—
    reasonstring (4–2000 chars)required—
    evidenceobject[]optional—
    /v1/bounties/{id}/disputes responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/bounties/{id}/cancelCancel a bounty. Poster/principal only, only from a cancellable state. Refunds if already funded.

    Auth: Session cookie or API key

    /v1/bounties/{id}/cancel parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/bounties/{id}/cancel responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    get/v1/admin/disputesCurator/admin dispute-review queue

    Every dispute joined with its bounty title/price, newest first. `status` narrows to open/resolved; omitted returns every dispute.

    Auth: Session cookie

    /v1/admin/disputes parameters
    NameInTypeRequiredDescription
    statusquery"open" | "resolved"optional—
    /v1/admin/disputes responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/disputes/{id}/resolveCurator/admin resolves a dispute: release, partial, or refund

    Real Stripe refund/release call per outcome — honestly blocked (503) while unconfigured. Writes the real ledger_entries row(s). Idempotency-Key required.

    Auth: Session cookie

    /v1/admin/disputes/{id}/resolve parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    outcome"release" | "partial" | "refund"required—
    outcomeAmountMinorinteger (−∞–9007199254740991)optional—
    /v1/admin/disputes/{id}/resolve responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    period
    path
    string
    required
    —
    /v1/rankings/{period} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/rankings/{period}.csvRankings report CSV (redirect)

    Redirects to the real, already-generated CSV for this period (`rankings_reports.csvDocumentId`) — the same file the worker job rendered, byte-identical on every download. 404 if no report has been published for this period; 503 if storage is not configured.

    Auth: Public — no session or key required

    /v1/rankings/{period}.csv parameters
    NameInTypeRequiredDescription
    periodpathstringrequired—
    /v1/rankings/{period}.csv responses
    StatusContent typeDescription
    303—Redirect to the real file in storage
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    get/v1/rankings/{period}.pdfRankings report PDF (redirect)

    Redirects to the real, already-generated PDF for this period (`rankings_reports.pdfDocumentId`), never generated on request. 404 if no report has been published for this period; 503 if storage is not configured.

    Auth: Public — no session or key required

    /v1/rankings/{period}.pdf parameters
    NameInTypeRequiredDescription
    periodpathstringrequired—
    /v1/rankings/{period}.pdf responses
    StatusContent typeDescription
    303—Redirect to the real file in storage
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    path
    string (uuid)
    required
    —
    /v1/datasets/{id}/download responses
    StatusContent typeDescription
    303—Redirect to the real file in storage
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    application/json
    Default Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/applications/draftsList the caller's drafts

    Auth: Session cookie

    /v1/applications/drafts parameters
    NameInTypeRequiredDescription
    opportunityIdquerystring (1–200 chars)optional—
    /v1/applications/drafts responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/applications/{opportunityId}/outcomeReport an outcome for an opportunity you applied to

    `sharedToRegistry` defaults `false` — explicit opt-in. Idempotency-Key required.

    Auth: Session cookie

    /v1/applications/{opportunityId}/outcome parameters
    NameInTypeRequiredDescription
    opportunityIdpathstring (1–200 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    status"applied" | "accepted" | "rejected" | "paid" | "scammed"required—
    draftIdstring (uuid)optional—
    notestring (0–2000 chars)optional—
    sharedToRegistrybooleanoptional—
    /v1/applications/{opportunityId}/outcome responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    Default Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/billing/pricingReal Stripe Prices/Products for the public pricing page

    Honestly `stripe-unconfigured` (503) when no Stripe key exists in this environment — the pricing page renders that state, never invented numbers.

    Auth: Public — no session or key required

    /v1/billing/pricing responses
    StatusContent typeDescription
    200application/jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    get/v1/billing/subscriptionThe signed-in user's own Earner Plus subscription

    Their most recent real `subscriptions` row, whatever its status — `null` when they have never subscribed. Earner Plus is always per-user (never org-billed); this never reads another user's row.

    Auth: Session cookie

    /v1/billing/subscription responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/billing/checkoutReal Stripe Checkout Session URL for Earner Plus

    `priceId` must be one of the real, currently-active ids `GET /v1/billing/pricing` just listed — any other id, even syntactically valid, is rejected so a client can never charge an arbitrary Stripe price. Creates/reuses the caller's `stripe_customers` row. Idempotency-Key required.

    Auth: Session cookie

    /v1/billing/checkout parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    priceIdstring (1–∞ chars)required—
    /v1/billing/checkout responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    post/v1/billing/portalReal Stripe Customer Portal URL — the one-click-cancel channel

    Requires an existing `stripe_customers` row (subscribe via POST /v1/billing/checkout first). Idempotency-Key required.

    Auth: Session cookie

    /v1/billing/portal parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/billing/portal responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    post/v1/billing/webhookReal Stripe Billing webhook receiver (distinct signing secret from Connect's webhook)

    Verifies the real signature against STRIPE_BILLING_WEBHOOK_SECRET, then for each of SUBSCRIPTION_CONFIRMATION_EVENTS sends the real confirmation email and upserts subscriptions/stripe_customers, validating every status write through assertTransition first. An illegal transition is logged and rejected with 500 so Stripe retries — never silently applied.

    Auth: Public — no session or key required

    /v1/billing/webhook parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/billing/webhook responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonRate limited — retry after the `Retry-After` seconds.
    500application/problem+jsonDefault Response
    503application/problem+jsonDefault Response
    query
    string (1–120 chars)
    optional
    —
    riskTolerancequery"conservative" | "moderate" | "aggressive"optional—
    payoutSpeedNeededquery"same_day" | "weekly" | "monthly" | "any"optional—
    minAcceptableNetHourlyAmountMinorqueryinteger (0–9007199254740991)optional—
    hoursPerWeekqueryinteger (0–168)optional—
    capitalAvailableAmountMinorqueryinteger (0–9007199254740991)optional—
    /v1/discover responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/discover/{id}One opportunity's full match explanation plus its registry record

    Auth: Session cookie

    /v1/discover/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    /v1/discover/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/discover/{id}/similarSimilar opportunities (same kind), ranked the same way as the feed

    Auth: Session cookie

    /v1/discover/{id}/similar parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    /v1/discover/{id}/similar responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/discover/{id}/saveSave an opportunity

    Idempotency-Key required.

    Auth: Session cookie

    /v1/discover/{id}/save parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/discover/{id}/save responses
    StatusContent typeDescription
    204—Default Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    delete/v1/discover/{id}/saveRemove a save

    Auth: Session cookie

    /v1/discover/{id}/save parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/discover/{id}/save responses
    StatusContent typeDescription
    204—Default Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/discover/{id}/hideHide an opportunity, with an optional reason

    Idempotency-Key required.

    Auth: Session cookie

    /v1/discover/{id}/hide parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    reasonstring (0–500 chars)optional—
    /v1/discover/{id}/hide responses
    StatusContent typeDescription
    204—Default Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    delete/v1/discover/{id}/hideUn-hide an opportunity

    Auth: Session cookie

    /v1/discover/{id}/hide parameters
    NameInTypeRequiredDescription
    idpathstring (1–200 chars)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/discover/{id}/hide responses
    StatusContent typeDescription
    204—Default Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    400
    application/problem+json
    Default Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/admin/link-submissionsEvery real link submission, oldest first

    Same keyset-pagination convention as `GET /v1/admin/appeals` and `GET /v1/admin/moderation/queue`.

    Auth: Session cookie

    /v1/admin/link-submissions parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    /v1/admin/link-submissions responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/link-submissions/{id}/reviewMark a link submission reviewed (queued -> reviewed)

    Auth: Session cookie

    /v1/admin/link-submissions/{id}/review parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/admin/link-submissions/{id}/review responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/link-submissions/{id}/dismissDismiss a link submission — no action warranted

    Auth: Session cookie

    /v1/admin/link-submissions/{id}/dismiss parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/admin/link-submissions/{id}/dismiss responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    —
    /v1/admin/moderation/queue responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/moderation/{reportId}/queuePick up a report for review (open -> queued)

    Real `open -> queued` transition via `@openmoney/trust-safety`'s `assertTransition`. Idempotency-Key required.

    Auth: Session cookie

    /v1/admin/moderation/{reportId}/queue parameters
    NameInTypeRequiredDescription
    reportIdpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/admin/moderation/{reportId}/queue responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/moderation/{reportId}/decideRecord a real human moderation decision (queued -> resolved)

    Writes a real `moderation_decisions` row with `reviewerUserId: req.auth.user.id` — `assertHumanReviewed` runs before the insert (defense-in-depth alongside the schema's own `NOT NULL`; PRD §7.2's adverse-action rule: every decision here is a real, attributed human judgment, never automated). Real `queued -> resolved` transition. Idempotency-Key required.

    Auth: Session cookie

    /v1/admin/moderation/{reportId}/decide parameters
    NameInTypeRequiredDescription
    reportIdpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    decision"no_action" | "warn" | "restrict" | "remove"required—
    templatedReasonstring (1–2000 chars)required—
    evidenceModerationEvidenceItemInput[]optional—
    /v1/admin/moderation/{reportId}/decide responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/moderation/{reportId}/dismissDismiss a report — no action warranted (open|queued -> dismissed)

    Real `open|queued -> dismissed` transition. The real dismissal reason is written to the audit log's own `reason` field (`reports` has no separate reason column). Idempotency-Key required.

    Auth: Session cookie

    /v1/admin/moderation/{reportId}/dismiss parameters
    NameInTypeRequiredDescription
    reportIdpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    reasonstring (1–2000 chars)required—
    /v1/admin/moderation/{reportId}/dismiss responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/moderation/{decisionId}/appealAppeal a real moderation decision — the decision's report's reporter, the bounty's real principal (when the decision's report targeted a bounty), or an admin/curator

    Session auth. Who may appeal is deliberately scoped to what this schema can really verify: the report's own `reporterUserId`, the real principal of the bounty when `subjectType` is `"bounty"` (`bountyPostedByPrincipal`, reused from `services/settlement.ts`), or an admin/curator acting on someone's behalf — a provider/opportunity/message/proof_presentation report has no single real per-user owner in this schema to check against. Creates a real `appeals` row, `appellantUserId: req.auth.user.id`. Idempotency-Key required.

    Auth: Session cookie

    /v1/admin/moderation/{decisionId}/appeal parameters
    NameInTypeRequiredDescription
    decisionIdpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    reasonstring (4–2000 chars)required—
    /v1/admin/moderation/{decisionId}/appeal responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/admin/appealsAppeals queue: every real appeal, oldest first

    Real `appeals` rows, oldest-`createdAt`-first (same keyset-pagination convention as `GET /v1/admin/moderation/queue` and `GET /v1/admin/audit`). Documented gap: `docs/api/phase-7-contract.md` only spells out `POST /v1/admin/appeals/{appealId}/resolve`, not this listing route — added because `apps/web`'s admin/curator appeals queue (`AppealsQueue.tsx`) needs a real list to resolve against and none existed.

    Auth: Session cookie

    /v1/admin/appeals parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    /v1/admin/appeals responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/admin/appeals/{appealId}/resolveResolve an appeal with a real, different reviewer

    Real `assertAppealReviewerDiffers` check before writing — a real 403 (`appeal-same-reviewer`) if the caller made the original decision (PRD §7.2: "appeals with a different reviewer"). The resolver's own rationale (`reason`) is written to the audit log, alongside the appellant's original `appeals.reason`, which is left untouched. Idempotency-Key required.

    Auth: Session cookie

    /v1/admin/appeals/{appealId}/resolve parameters
    NameInTypeRequiredDescription
    appealIdpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    outcome"upheld" | "overturned" | "partial"required—
    reasonstring (1–2000 chars)required—
    /v1/admin/appeals/{appealId}/resolve responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    providerIdstring (1–200 chars)optional—
    providerLabelstring (1–200 chars)required—
    connectionType"csv" | "pdf" | "screenshot" | "manual"requiredSources created directly by the caller; email/argyle/pinwheel sources are created by their own connect/callback/webhook routes.
    /v1/passport/sources responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    delete/v1/passport/sources/{id}Disconnect a source

    Sets status to `disconnected`; does not delete its earnings_events (use Settings -> Data delete for that).

    Auth: Session cookie

    /v1/passport/sources/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/passport/sources/{id} responses
    StatusContent typeDescription
    204—Default Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/passport/sources/{id}/import/csvImport a CSV/XLSX statement, or confirm a low-confidence column mapping

    Two call shapes: a multipart upload (field `file`) to parse a new statement, or a JSON body `{ documentId, mapping, model, promptVersion }` (the exact values a prior low-confidence response returned) to apply a human-reviewed mapping with no further LLM call. Idempotency-Key required.

    Auth: Session cookie

    /v1/passport/sources/{id}/import/csv parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/passport/sources/{id}/import/csv responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    415application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/passport/sources/{id}/import/pdfImport a PDF statement (multipart upload)

    Auth: Session cookie

    /v1/passport/sources/{id}/import/pdf parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/passport/sources/{id}/import/pdf responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    415application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/passport/sources/{id}/import/screenshotImport a screenshot/photo of a statement (multipart upload)

    Strips EXIF, runs vision extraction, and a real error-level-analysis edited-image heuristic. Always writes confirmedByUser: false, extractionConfidence <= 0.6 (PRD §2.3).

    Auth: Session cookie

    /v1/passport/sources/{id}/import/screenshot parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/passport/sources/{id}/import/screenshot responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    415application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/passport/sources/gmail/connectStart the Gmail OAuth flow (gmail.readonly, label-scoped)

    503 with `{ integration: "google", mode: "unconfigured", howTo }` when GOOGLE_CLIENT_ID/SECRET are not set — never a fabricated authorization URL.

    Auth: Session cookie

    /v1/passport/sources/gmail/connect parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/passport/sources/gmail/connect responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    get/v1/passport/sources/gmail/callbackOAuth redirect target

    On success creates an income_sources row (connectionType: email) and a consent_events row (scope: import:gmail), then redirects back to the app.

    Auth: Public — no session or key required

    /v1/passport/sources/gmail/callback parameters
    NameInTypeRequiredDescription
    codequerystringoptional—
    statequerystringoptional—
    errorquerystringoptional—
    /v1/passport/sources/gmail/callback responses
    StatusContent typeDescription
    303—Default Response
    400application/problem+jsonDefault Response
    429application/problem+jsonRate limited — retry after the `Retry-After` seconds.
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    post/v1/passport/sources/argyle/connectCreate a real Argyle sandbox user and Link token

    503 with `{ integration: "argyle", mode: "unconfigured", howTo }` when ARGYLE_CLIENT_ID/SECRET are not set.

    Auth: Session cookie

    /v1/passport/sources/argyle/connect parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/passport/sources/argyle/connect responses
    StatusContent typeDescription
    201application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    post/v1/passport/sources/argyle/webhookReal Argyle webhook receiver (HMAC-SHA512 signature-verified)

    Auth: Public — no session or key required

    /v1/passport/sources/argyle/webhook parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/passport/sources/argyle/webhook responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonRate limited — retry after the `Retry-After` seconds.
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    post/v1/passport/sources/pinwheel/connectCreate a real Pinwheel sandbox Link token

    503 with `{ integration: "pinwheel", mode: "unconfigured", howTo }` when PINWHEEL_API_SECRET is not set.

    Auth: Session cookie

    /v1/passport/sources/pinwheel/connect parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/passport/sources/pinwheel/connect responses
    StatusContent typeDescription
    201application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    post/v1/passport/sources/pinwheel/webhookReal Pinwheel webhook receiver (HMAC-SHA256 signature-verified)

    Auth: Public — no session or key required

    /v1/passport/sources/pinwheel/webhook parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/passport/sources/pinwheel/webhook responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonRate limited — retry after the `Retry-After` seconds.
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    get/v1/passport/eventsList the caller's earnings_events

    Auth: Session cookie

    /v1/passport/events parameters
    NameInTypeRequiredDescription
    providerIdquerystring (1–200 chars)optional—
    incomeSourceIdquerystring (uuid)optional—
    fromquerystring (date-time)optional—
    toquerystring (date-time)optional—
    needsConfirmationquerybooleanoptional—
    cursorquerystringoptional—
    limitqueryinteger (1–200)optional—
    /v1/passport/events responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/passport/events/export.csvStream the caller's full ledger as CSV

    Auth: Session cookie

    /v1/passport/events/export.csv responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/passport/events/{id}Full row including a signed URL to the source document

    Auth: Session cookie

    /v1/passport/events/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    /v1/passport/events/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    patch/v1/passport/events/{id}Edit or confirm one earnings event

    Any editable field subset plus `confirm`. Any successful call always sets confirmedByUser: true and extractionConfidence: null — a human correction is not a model confidence.

    Auth: Session cookie

    /v1/passport/events/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    occurredAtstring (date-time)optional—
    periodStartstring (date-time) | nulloptional—
    periodEndstring (date-time) | nulloptional—
    currencystringoptionalISO 4217 currency code, e.g. USD, INR.
    grossAmountMinorinteger (-9007199254740991–9007199254740991)optional—
    tipsAmountMinorinteger (-9007199254740991–9007199254740991)optional—
    platformFeesAmountMinorinteger (-9007199254740991–9007199254740991)optional—
    otherFeesAmountMinorinteger (-9007199254740991–9007199254740991)optional—
    reimbursementsAmountMinorinteger (-9007199254740991–9007199254740991)optional—
    hoursActivenumber | nulloptional—
    hoursOnlinenumber | nulloptional—
    tasksinteger (-9007199254740991–9007199254740991) | nulloptional—
    milesnumber | nulloptional—
    confirmbooleanoptional—
    /v1/passport/events/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    delete/v1/passport/events/{id}Remove a single wrongly-imported row

    Auth: Session cookie

    /v1/passport/events/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/passport/events/{id} responses
    StatusContent typeDescription
    204—Default Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/passport/analytics/overviewLive analytics over the caller's earnings_events

    Runs @openmoney/scoring's passport engine live (never a cached row) — always current.

    Auth: Session cookie

    /v1/passport/analytics/overview parameters
    NameInTypeRequiredDescription
    windowquery"7d" | "30d" | "90d" | "12m"optional—
    providerIdquerystring (1–200 chars)optional—
    /v1/passport/analytics/overview responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/passport/analytics/by-platformOne PassportAnalytics per provider the caller has events for

    Auth: Session cookie

    /v1/passport/analytics/by-platform parameters
    NameInTypeRequiredDescription
    windowquery"7d" | "30d" | "90d" | "12m"optional—
    /v1/passport/analytics/by-platform responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/passport/analytics/counterfactualThe caller's own net-hourly p50 against the registry's published evidence for the same provider

    Auth: Session cookie

    /v1/passport/analytics/counterfactual parameters
    NameInTypeRequiredDescription
    providerIdquerystring (1–200 chars)required—
    windowquery"7d" | "30d" | "90d" | "12m"optional—
    /v1/passport/analytics/counterfactual responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/passport/analytics/refreshAdmin/worker-triggered: recompute and upsert derived_metrics for one user

    Not user-facing — the nightly aggregation job calls this so it has a stable snapshot instead of recomputing every user live.

    Auth: Session cookie

    /v1/passport/analytics/refresh parameters
    NameInTypeRequiredDescription
    userIdquerystring (uuid)required—
    /v1/passport/analytics/refresh responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/passport/exportEnqueue a zip export (JSON + CSV + original documents)

    Idempotency-Key required. The worker (apps/worker/src/jobs/passport-export.ts) processes the job and marks the row ready with a signed download URL.

    Auth: Session cookie

    /v1/passport/export parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/passport/export responses
    StatusContent typeDescription
    202application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/passport/export/{id}Poll export status

    Auth: Session cookie

    /v1/passport/export/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    /v1/passport/export/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/passport/deleteStart the two-step deletion of every Passport row and stored document for the caller

    Body `confirmationPhrase` must equal the caller's account email (first factor). Idempotency-Key required. Sends a single-use confirmation link to that email (second factor, PRD §2.5) and returns its request id immediately; nothing is deleted, and the worker job (apps/worker/src/jobs/passport-delete.ts) is not enqueued, until POST /v1/passport/delete/confirm consumes that link.

    Auth: Session cookie

    /v1/passport/delete parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    confirmationPhrasestring (1–320 chars)requiredMust equal the caller's account email, checked server-side.
    /v1/passport/delete responses
    StatusContent typeDescription
    202application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/passport/delete/confirmConsume a Passport-deletion confirmation link and enqueue the real delete job

    No session required, same as GET /v1/auth/magic-link/consume — the single-use token from the confirmation email is itself the proof, since the email may be opened on a device that never had a session here. Only on `status: "deleted"` is apps/worker/src/jobs/passport-delete.ts actually enqueued.

    Auth: Public — no session or key required

    /v1/passport/delete/confirm parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    tokenstring (1–∞ chars)required—
    /v1/passport/delete/confirm responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/passport/consent-logThe caller's consent_events rows

    Auth: Session cookie

    /v1/passport/consent-log parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    /v1/passport/consent-log responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    put/v1/passport/consent/aggregateToggle the aggregate:anonymized consent scope (default off)

    Gates whether the nightly aggregation job (apps/worker/src/jobs/passport-aggregate.ts) may include this user's data. Idempotency-Key required.

    Auth: Session cookie

    /v1/passport/consent/aggregate parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    optInbooleanrequired—
    /v1/passport/consent/aggregate responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    optional
    UUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    locationCountrystring (2–2 chars) | nulloptional—
    locationRegionstring (1–120 chars) | nulloptional—
    locationCitystring (1–120 chars) | nulloptional—
    workModes"remote" | "onsite" | "hybrid"[]optional—
    hoursPerWeekinteger (0–168) | nulloptional—
    timeWindowsobject[]optional—
    hasVehicleboolean | nulloptional—
    equipmentstring (1–80 chars)[]optional—
    credentialsHeldstring (1–80 chars)[]optional—
    backgroundCheckWillingboolean | nulloptional—
    minAcceptableNetHourlyAmountMinorinteger (0–9007199254740991) | nulloptional—
    minAcceptableNetHourlyCurrencystring | nulloptional—
    payoutSpeedNeeded"same_day" | "weekly" | "monthly" | "any"optional—
    capitalAvailableAmountMinorinteger (0–9007199254740991)optional—
    capitalAvailableCurrencystringoptionalISO 4217 currency code, e.g. USD, INR.
    riskTolerance"conservative" | "moderate" | "aggressive"optional—
    jurisdictionsstring (2–2 chars)[]optional—
    /v1/profile responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/profile/skillsList typed and Passport-inferred skills

    Auth: Session cookie

    /v1/profile/skills responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/profile/skillsAdd a typed skill

    The user typed this themselves, so it is confirmed immediately (`source: "typed"`, `confirmed: true`). Idempotency-Key required.

    Auth: Session cookie

    /v1/profile/skills parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    labelstring (1–120 chars)required—
    /v1/profile/skills responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/profile/skills/inferInfer skills from confirmed Passport earnings

    Reads confirmed `earnings_events`, maps each provider to its registry `categories[]`, and inserts any not already present as `source: "passport_inferred"`, `confirmed: false`. Idempotent by `(userId, label)` — safe to call repeatedly. Idempotency-Key required.

    Auth: Session cookie

    /v1/profile/skills/infer parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/profile/skills/infer responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/profile/skills/{id}/confirmConfirm a skill

    Never auto-confirmed anywhere else. Idempotency-Key required.

    Auth: Session cookie

    /v1/profile/skills/{id}/confirm parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/profile/skills/{id}/confirm responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    delete/v1/profile/skills/{id}Dismiss a skill (typed or inferred)

    Auth: Session cookie

    /v1/profile/skills/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/profile/skills/{id} responses
    StatusContent typeDescription
    204—Default Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/profile/interestsList interest tags

    Auth: Session cookie

    /v1/profile/interests responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/profile/interestsAdd an interest tag

    Free text, never a protected characteristic (PRD §4.3's fairness control). Idempotency-Key required.

    Auth: Session cookie

    /v1/profile/interests parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    tagstring (1–80 chars)required—
    /v1/profile/interests responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    delete/v1/profile/interests/{id}Remove an interest tag

    Auth: Session cookie

    /v1/profile/interests/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/profile/interests/{id} responses
    StatusContent typeDescription
    204—Default Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.

    Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/orgs/{orgId}/proof-policies parameters
    NameInTypeRequiredDescription
    orgIdpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    namestring (1–120 chars)required—
    requirementsobject[]required—
    /v1/orgs/{orgId}/proof-policies responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/orgs/{orgId}/webhooksList webhook endpoints (never returns signingSecret again)

    Auth: Session cookie

    /v1/orgs/{orgId}/webhooks parameters
    NameInTypeRequiredDescription
    orgIdpathstring (uuid)required—
    /v1/orgs/{orgId}/webhooks responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/orgs/{orgId}/webhooksCreate a webhook endpoint

    Generates and returns `signingSecret` ONCE — never retrievable again, same convention as an API key's raw value. Requires `Idempotency-Key`.

    Auth: Session cookie

    /v1/orgs/{orgId}/webhooks parameters
    NameInTypeRequiredDescription
    orgIdpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    urlstring (uri)required—
    events"proof.verified" | "proof.revoked" | "proof.expired"[]required—
    /v1/orgs/{orgId}/webhooks responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/orgs/{orgId}/webhooks/{id}/deliveriesRecent deliveries for one webhook endpoint (audit/debugging)

    Auth: Session cookie

    /v1/orgs/{orgId}/webhooks/{id}/deliveries parameters
    NameInTypeRequiredDescription
    limitqueryinteger (1–200)optional—
    orgIdpathstring (uuid)required—
    idpathstring (uuid)required—
    /v1/orgs/{orgId}/webhooks/{id}/deliveries responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/orgs/{orgId}/proof-verificationsThe org's own proof_verification_events (usage dashboard)

    Auth: Session cookie

    /v1/orgs/{orgId}/proof-verifications parameters
    NameInTypeRequiredDescription
    cursorquerystringoptional—
    limitqueryinteger (1–100)optional—
    orgIdpathstring (uuid)required—
    /v1/orgs/{orgId}/proof-verifications responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/proofsList the caller's issued proofs

    No `sdJwt` in the list view — fetch GET /v1/proofs/{id} for the full credential.

    Auth: Session cookie

    /v1/proofs responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/proofsIssue a new Proof from real Passport/registry/identity data

    Runs the matching claim computer (`@openmoney/proof`'s claims/*). 409 `insufficient-evidence` when it finds no real evidence to issue this type honestly; 409 `holder-key-required` when the caller has not registered a holder public key yet (`POST /v1/proofs/holder-key` first); 503 when the type needs an integration that is not configured in this environment (identity -> Stripe Identity). Idempotency-Key required.

    Auth: Session cookie

    /v1/proofs parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    type"identity" | "active_earner" | "earnings" | "reliability" | "skill" | "platform_standing"required—
    /v1/proofs responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    503application/problem+jsonDefault Response
    post/v1/proofs/holder-keyRegister/update the session's current holder public key

    The matching private key is generated and held entirely in the browser (WebCrypto) and never sent here (A3.4) — only its public half, embedded in every newly issued credential's `cnf` claim from now on. Idempotency-Key required.

    Auth: Session cookie

    /v1/proofs/holder-key parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    publicJwkJwkInputrequired—
    /v1/proofs/holder-key responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/proofs/{id}Full record including the real, all-disclosures sdJwt

    Only the owner may fetch this — it's what the browser needs to build a selective presentation locally via presentProof.

    Auth: Session cookie

    /v1/proofs/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    /v1/proofs/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/proofs/{id}/revokeRevoke a proof

    Flips its Status List bit and re-signs that issuer key's Status List Token SYNCHRONOUSLY in this handler (A3.3's 60-second propagation is trivially met by not queuing this step). Enqueues real proof.revoked webhook deliveries to every org that has previously verified this proof. Idempotency-Key required.

    Auth: Session cookie

    /v1/proofs/{id}/revoke parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/proofs/{id}/revoke responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    409application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/proofs/{id}/sharesCreate a share link (bookkeeping only — the presentation itself is built client-side, never uploaded)

    Auth: Session cookie

    /v1/proofs/{id}/shares parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    audiencestring (1–2000 chars)optional—
    expiresInSecondsinteger (−∞–2592000)required—
    disclosedClaimPathsstring (1–∞ chars)[]required—
    /v1/proofs/{id}/shares responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/proofs/shares/{id}Public metadata for a share link — never claim values

    For a relying-party page to render "what this share claims to be" before a live verification happens.

    Auth: Public — no session or key required

    /v1/proofs/shares/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    /v1/proofs/shares/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/proofs/{id}/verification-logThe caller's own verification events for this proof ("who verified what, when")

    Hash-only — never re-derives what was disclosed beyond the disclosedClaimPaths array already logged.

    Auth: Session cookie

    /v1/proofs/{id}/verification-log parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    /v1/proofs/{id}/verification-log responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/proofs/verify/nonceMint a real single-use nonce a presentation request must embed (the actual replay defense, A3.3)

    Auth: Public — no session or key required

    /v1/proofs/verify/nonce parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    audiencestring (1–2000 chars)required—
    /v1/proofs/verify/nonce responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/proofs/verifyVerify a presentation

    Rejects a nonce not found freshly un-consumed in `proof_nonces` with 400 (A3.3 replay defense — server-side tracking, not just the presentation's own internal nonce field). Writes one proof_verification_events row (hash of the presentation, never the presentation itself) regardless of outcome. When called with a proof:verify-scoped API key, enqueues real proof.verified webhook deliveries for that key's org. This is the exact route the SDK, the embeddable widget, the hosted verify page, and the console's paste-a-presentation tool all call (A3.1).

    Auth: Public — no session or key required

    /v1/proofs/verify parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    presentationstring (1–∞ chars)required—
    audiencestring (1–2000 chars)required—
    noncestring (1–∞ chars)required—
    /v1/proofs/verify responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/proofs/status-list/{kid}Live-built, real signed Status List Token for one issuer key

    Auth: Public — no session or key required

    /v1/proofs/status-list/{kid} parameters
    NameInTypeRequiredDescription
    kidpathstring (1–500 chars)required—
    /v1/proofs/status-list/{kid} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/proofs/requestsCreate a "request a Proof" link an earner can open and fulfil

    Idempotency-Key required.

    Auth: API key (Bearer)

    /v1/proofs/requests parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    policyIdstring (uuid)optional—
    expiresInSecondsinteger (−∞–2592000)required—
    /v1/proofs/requests responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    403application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/proofs/requests/{id}Status of a request-a-Proof link

    Auth: Public — no session or key required

    /v1/proofs/requests/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    /v1/proofs/requests/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/.well-known/did.jsondid:web document over every issuer key this deployment has ever signed with

    Auth: Public — no session or key required

    /.well-known/did.json responses
    StatusContent typeDescription
    200application/jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/.well-known/jwks.jsonPlain JWKS — what most SD-JWT VC verifiers (including our own SDK) actually fetch

    Auth: Public — no session or key required

    /.well-known/jwks.json responses
    StatusContent typeDescription
    200application/jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    —
    detailstring (1–5000 chars)required—
    /v1/reports responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    subjectType"opportunity" | "provider" | "search"required—
    opportunityIdstring (1–200 chars)optional—
    providerIdstring (1–200 chars)optional—
    searchQueryobjectoptional—
    channels"web_push" | "email"[]optional—
    digest"immediate" | "daily_digest" | "weekly_digest"optional—
    /v1/watches responses
    StatusContent typeDescription
    201application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    patch/v1/watches/{id}Update channels, digest, or pause state

    Idempotency-Key required.

    Auth: Session cookie

    /v1/watches/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    channels"web_push" | "email"[]optional—
    digest"immediate" | "daily_digest" | "weekly_digest"optional—
    pausedbooleanoptional—
    /v1/watches/{id} responses
    StatusContent typeDescription
    200application/jsonDefault Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    delete/v1/watches/{id}Remove a watch

    Auth: Session cookie

    /v1/watches/{id} parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.
    /v1/watches/{id} responses
    StatusContent typeDescription
    204—Default Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    get/v1/watches/{id}/eventsReal change history for a watch's subject

    Opportunity/provider-type watches: real `watch_events` rows. Search-type watches: new-match history from `alert_deliveries` where `kind = "new_match"`.

    Auth: Session cookie

    /v1/watches/{id}/events parameters
    NameInTypeRequiredDescription
    idpathstring (uuid)required—
    /v1/watches/{id}/events responses
    StatusContent typeDescription
    200application/jsonDefault Response
    401application/problem+jsonDefault Response
    404application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    post/v1/push-subscriptionsRegister a Web Push subscription

    The browser Push API's own `PushSubscriptionJSON` shape. Upserts by `endpoint`. Idempotency-Key required.

    Auth: Session cookie

    /v1/push-subscriptions parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    endpointstring (uri)required—
    keysobjectrequired—
    /v1/push-subscriptions responses
    StatusContent typeDescription
    204—Default Response
    400application/problem+jsonDefault Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.
    delete/v1/push-subscriptionsUnregister a Web Push subscription

    Called on `pushsubscriptionchange`/unsubscribe from the client.

    Auth: Session cookie

    /v1/push-subscriptions parameters
    NameInTypeRequiredDescription
    Idempotency-Keyheaderstring (uuid)optionalUUID chosen by the client. The server rejects mutating requests that omit it (400 `idempotency-key-required`); the generated SDK (@openmoney/api-client) adds one automatically, which is why it is not marked required here. The first response is stored for 24 hours and replayed for the same key with `Idempotent-Replayed: true`.

    Request body · application/json

    Request body fields
    FieldTypeRequiredDescription
    endpointstring (uri)required—
    /v1/push-subscriptions responses
    StatusContent typeDescription
    204—Default Response
    401application/problem+jsonDefault Response
    429application/problem+jsonDefault Response
    500application/problem+jsonUnexpected error. The `requestId` and `traceId` identify the failure in logs.