{"openapi":"3.1.0","info":{"title":"TrackOrigin Verification API","description":"Verifiable provenance for human-made music. Artists complete live ML-scored challenges; passing sessions produce an Ed25519-signed certificate that DSPs, labels and distributors can verify offline.\n\n**Authentication.** Pass `Authorization: Bearer to_pk_live_…` (read-only, browser-safe) or `to_sk_live_…` (server-side, all endpoints). Test keys `to_pk_test_…` / `to_sk_test_…` hit the sandbox.\n\n**Verification.** Every certificate body is canonical JSON signed with Ed25519. Pull the active key from `/.well-known/trackorigin-public-key.json` or the full key history from `/.well-known/trackorigin-public-keys.json` and verify locally — no callback to us is required.\n\n**Catalogue sync.** Use `GET /api/v1/certificates/changes` for issued+revoked deltas; subscribe to `cert.issued` / `cert.revoked` webhooks for push delivery (see the `webhooks` tag for the signing scheme).","contact":{"name":"TrackOrigin","url":"https://trackorigin.io/developers"},"license":{"name":"TrackOrigin API Terms","url":"https://trackorigin.io/terms"},"version":"0.1.0"},"paths":{"/v1/healthz":{"get":{"summary":"Healthz","operationId":"healthz_v1_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Healthz V1 Healthz Get"}}}}}}},"/v1/readyz":{"get":{"summary":"Readyz","operationId":"readyz_v1_readyz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Readyz V1 Readyz Get"}}}}}}},"/.well-known/trackorigin-pubkey.pem":{"get":{"summary":"Public Pubkey","operationId":"public_pubkey__well_known_trackorigin_pubkey_pem_get","responses":{"200":{"description":"Successful Response"}}}},"/v1/auth/signup":{"post":{"summary":"Signup","operationId":"signup_v1_auth_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupBody"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Signup V1 Auth Signup Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me":{"get":{"summary":"Get Me","description":"Return the current artist's full profile for editing.","operationId":"get_me_v1_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Me V1 Me Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/handle":{"patch":{"summary":"Update Me Handle","description":"Set or change the artist's public handle.\n\nSelf-serve, but only **until the first certificate is issued** — at that\npoint the handle is baked into a signed manifest and can't change without\nre-issuing every cert, so it locks (change then requires support).","operationId":"update_me_handle_v1_me_handle_patch","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandleUpdateBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Update Me Handle V1 Me Handle Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/ml-training":{"patch":{"summary":"Update Ml Training Opt Out","description":"Flip the \"Improve TrackOrigin from my uploads\" setting.\n\nTied to clause 5.4 of the Terms / §2 of the License Summary. The Privacy\nAct / ACL transparency case for the setting depends on the artist being\nable to switch it off cheaply and visibly, so this is a first-class\nendpoint rather than a checkbox buried in a generic update payload.","operationId":"update_ml_training_opt_out_v1_me_ml_training_patch","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MLOptOutBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Update Ml Training Opt Out V1 Me Ml Training Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/profile":{"patch":{"summary":"Update Me Profile","description":"Update bio, location, external_links.\n\ndisplay_name and handle are not on this surface (see ProfileUpdateBody);\npydantic's extra=\"forbid\" on TOBase rejects any client trying to send them.","operationId":"update_me_profile_v1_me_profile_patch","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileUpdateBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Update Me Profile V1 Me Profile Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/avatar":{"post":{"summary":"Upload Avatar","description":"Upload (or replace) the current artist's avatar.\n\nServer-side: validates MIME, caps size, decodes/re-encodes via cv2 (strips\nall metadata including EXIF/GPS), center-crops to square, resizes to\n512×512, stores as clean JPEG.","operationId":"upload_avatar_v1_me_avatar_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_avatar_v1_me_avatar_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Upload Avatar V1 Me Avatar Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Avatar","description":"Remove the current artist's avatar.","operationId":"delete_avatar_v1_me_avatar_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Delete Avatar V1 Me Avatar Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/export":{"get":{"summary":"Export My Data","description":"Download a JSON archive of personal information held about the caller.\n\nImplements Privacy Policy §15.1 (access). Returns the artist profile,\nevery track they uploaded with its declarations and sessions, the\nsubmissions metadata (no media bytes — those are downloadable from\n`/v1/storage/...` per-asset and not embeddable in a single JSON), the\ncredit ledger, and references to any certificates. Excludes internal\nfields like password hash, KYC embedding, and session face anchor.","operationId":"export_my_data_v1_me_export_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/delete":{"post":{"summary":"Delete My Account","description":"Permanently delete the calling artist's personal information.\n\nPrivacy §15.3 + §11.4: media, declarations and pre-cert sessions are\npurged; ISSUED certificates remain because they are signed public\nprovenance records anchored to the master hash, and erasing them\nwould invalidate every downstream verification. We sever the link\nbetween the artist and the cert by clearing the manifest's artist\nblock but preserve the signed body so the signature still verifies.\n\nHard-deleting an account when an active cert exists would also tear\na hole in the catalogue-sync feed for any DSP relying on us. Instead\nwe anonymise.","operationId":"delete_my_account_v1_me_delete_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAccountBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Delete My Account V1 Me Delete Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/public/artists/{handle}/avatar":{"get":{"summary":"Public Artist Avatar","description":"Serve an artist's avatar by handle. Returns 404 if no avatar set;\nfrontend falls back to initials. Caches aggressively in browsers and\nintermediaries since the URL is busted on every avatar replacement.","operationId":"public_artist_avatar_v1_public_artists__handle__avatar_get","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string","title":"Handle"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/logout":{"post":{"summary":"Logout","description":"Single-session logout. Clears all auth cookies, revokes this session's\naccess jti, and burns ONLY this session's refresh-token family (other\ndevices stay logged in — see /auth/logout-all for the all-device sweep).\n\nTolerates missing/expired auth — the cookie clear MUST always happen so the\nnav can never get stuck showing a logged-in state.","operationId":"logout_v1_auth_logout_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Logout V1 Auth Logout Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/logout-all":{"post":{"summary":"Logout All","description":"Explicit all-device logout. Revokes the current access jti and deletes\nEVERY refresh-token family for the artist, so all other sessions can no\nlonger rotate. Requires a valid session (CurrentArtist).","operationId":"logout_all_v1_auth_logout_all_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Logout All V1 Auth Logout All Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/login":{"post":{"summary":"Login","operationId":"login_v1_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Login V1 Auth Login Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/password-reset/request":{"post":{"summary":"Password Reset Request","description":"Begin a password reset. Always returns the same 200 body whether or not\nthe email exists — no account enumeration. When the account exists we mint a\nsingle-use token, persist ONLY its sha256 (so a DB leak can't be replayed),\nand hand the link to the delivery seam.","operationId":"password_reset_request_v1_auth_password_reset_request_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetRequestBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Password Reset Request V1 Auth Password Reset Request Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/password-reset/confirm":{"post":{"summary":"Password Reset Confirm","description":"Complete a password reset. Atomically consumes the single-use token,\nsets the new password, and revokes EVERY existing session (all refresh\nfamilies) so a stolen credential can't survive the reset. Existing access\ntokens lapse within the short access-token TTL.","operationId":"password_reset_confirm_v1_auth_password_reset_confirm_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetConfirmBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Password Reset Confirm V1 Auth Password Reset Confirm Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/verify-email/resend":{"post":{"summary":"Resend Verification Email","description":"Resend the signup verification email to the logged-in artist. Idempotent:\nif the address is already verified we just say so. Rate-limited to stop\nsomeone hammering the send endpoint.","operationId":"resend_verification_email_v1_auth_verify_email_resend_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Resend Verification Email V1 Auth Verify Email Resend Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/refresh":{"post":{"summary":"Refresh Token","description":"Route wrapper around the rotation logic below.\n\nOn any authentication failure (401) — expired/used/unknown refresh token,\nmissing rotation metadata, deleted artist — we clear every auth cookie on\nthe way out. This is the server half of the flicker fix: a browser can end\nup half-dead, with the readable `to_csrf` cookie still present while\n`to_at`/`to_rt` are invalid. Without this, the client keeps thinking it's\nauthed and ping-pongs between /login and gated pages. Clearing the cookies\nhere makes a dead session self-heal for every client, even before the JS\nfix loads, and without leaving stale `to_at`/`to_rt` cookies behind.\n\nThrottle rejections (429) and any non-401 error propagate unchanged so a\n*live* session that merely got rate-limited is never logged out.","operationId":"refresh_token_v1_auth_refresh_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RefreshTokenBody"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Refresh Token V1 Auth Refresh Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tracks":{"post":{"summary":"Upload Track","operationId":"upload_track_v1_tracks_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_track_v1_tracks_post"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Upload Track V1 Tracks Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"List Tracks","description":"List all tracks for the current artist.","operationId":"list_tracks_v1_tracks_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Tracks V1 Tracks Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tracks/{track_id}/identifiers":{"patch":{"summary":"Update Track Identifiers","description":"Set or clear ISRC / UPC / ISWC on a track that hasn't been certified yet.\n\n**Why pre-cert only:** the issued certificate is an Ed25519 signature over\na canonical-JSON body that includes `master.{isrc,upc,iswc}`. Changing the\nidentifier after signing would invalidate the signature without producing a\nnew audit-traceable cert — that's a reissuance, not an edit. For a\ncertified track, revoke the existing cert and run verification again.\n\n**Sparse semantics:** field absent → leave alone. Field empty string →\nclear. Field with a value → normalise + validate + set. ISRC/UPC/ISWC\neach have their own format regex (CC + reg + year + designation; 12 digits;\nT + 10 digits). Bad format → 400 with a precise reason.\n\n**Unique-ish ISRC:** indexes on `tracks.isrc` are sparse and non-unique\n(same ISRC can legitimately be re-released by different parties), so two\ntracks can carry the same ISRC. The certificate layer is the deduper.","operationId":"update_track_identifiers_v1_tracks__track_id__identifiers_patch","parameters":[{"name":"track_id","in":"path","required":true,"schema":{"type":"string","title":"Track Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackIdentifiersBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Update Track Identifiers V1 Tracks  Track Id  Identifiers Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tracks/{track_id}/delete":{"post":{"summary":"Delete Track Verification","description":"Permanently delete a non-certified verification and all related data.\n\nThis removes the track upload, declaration, sessions, challenges, submissions,\nchunks, and stored files. It does not delete audit logs.","operationId":"delete_track_verification_v1_tracks__track_id__delete_post","parameters":[{"name":"track_id","in":"path","required":true,"schema":{"type":"string","title":"Track Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteVerificationBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Delete Track Verification V1 Tracks  Track Id  Delete Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions":{"get":{"summary":"List Sessions","description":"List all sessions for the current artist.","operationId":"list_sessions_v1_sessions_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Sessions V1 Sessions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"summary":"Create Session","operationId":"create_session_v1_sessions_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Create Session V1 Sessions Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tracks/{track_id}":{"get":{"summary":"Get Track","operationId":"get_track_v1_tracks__track_id__get","parameters":[{"name":"track_id","in":"path","required":true,"schema":{"type":"string","title":"Track Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Track V1 Tracks  Track Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/declarations":{"post":{"summary":"Submit Declaration","operationId":"submit_declaration_v1_declarations_post","parameters":[{"name":"track_id","in":"query","required":true,"schema":{"type":"string","title":"Track Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeclarationCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Submit Declaration V1 Declarations Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/declarations/{declaration_id}":{"get":{"summary":"Get Declaration","description":"Fetch a declaration the artist owns, so /resolve can be deep-linked\n(resumed) from the dashboard without relying on sessionStorage carried over\nfrom the declaration step. Mirrors what the declaration POST returns.","operationId":"get_declaration_v1_declarations__declaration_id__get","parameters":[{"name":"declaration_id","in":"path","required":true,"schema":{"type":"string","title":"Declaration Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Declaration V1 Declarations  Declaration Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/declarations/{declaration_id}/resolve":{"post":{"summary":"Resolve Discrepancies","operationId":"resolve_discrepancies_v1_declarations__declaration_id__resolve_post","parameters":[{"name":"declaration_id","in":"path","required":true,"schema":{"type":"string","title":"Declaration Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DiscrepancyResolution"},"title":"Resolutions"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Resolve Discrepancies V1 Declarations  Declaration Id  Resolve Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/studio-check":{"post":{"summary":"Studio Check","operationId":"studio_check_v1_sessions__session_id__studio_check_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioCheckBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Studio Check V1 Sessions  Session Id  Studio Check Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/face-anchor":{"post":{"summary":"Set Face Anchor","description":"Capture the identity anchor from multiple lobby frames.\n\nThe browser uploads ~5 JPEG frames over ~2.5 seconds. We:\n  1. Run InsightFace on each, keeping the largest face per frame.\n  2. Require a face to be detected in at least 3 frames.\n  3. Reject if all detected bboxes are essentially identical (photo attack).\n  4. Average the embeddings into the session anchor.\n\nReturns {embedding_set: bool, reason?: str}.","operationId":"set_face_anchor_v1_sessions__session_id__face_anchor_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_set_face_anchor_v1_sessions__session_id__face_anchor_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Set Face Anchor V1 Sessions  Session Id  Face Anchor Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/start":{"post":{"summary":"Start Session","operationId":"start_session_v1_sessions__session_id__start_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Start Session V1 Sessions  Session Id  Start Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}":{"get":{"summary":"Get Session","operationId":"get_session_v1_sessions__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Session V1 Sessions  Session Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/challenges":{"get":{"summary":"List Challenges","operationId":"list_challenges_v1_sessions__session_id__challenges_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Challenges V1 Sessions  Session Id  Challenges Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/challenges/{challenge_id}":{"get":{"summary":"Get Challenge","operationId":"get_challenge_v1_sessions__session_id__challenges__challenge_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"challenge_id","in":"path","required":true,"schema":{"type":"string","title":"Challenge Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Challenge V1 Sessions  Session Id  Challenges  Challenge Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/challenges/{challenge_id}/reference-audio":{"get":{"summary":"Get Reference Audio","operationId":"get_reference_audio_v1_sessions__session_id__challenges__challenge_id__reference_audio_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"challenge_id","in":"path","required":true,"schema":{"type":"string","title":"Challenge Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/challenges/{challenge_id}/chunks":{"post":{"summary":"Upload Chunk","description":"Receive a single recorded chunk during an active challenge.\n\nRetry policy:\n    Each arrival of sequence=0 (with kind='audio_video') counts as a new\n    attempt. We track this server-side on the challenge document and cap\n    at MAX_ATTEMPTS to prevent infinite client-side retries until a\n    passing combination is produced.","operationId":"upload_chunk_v1_sessions__session_id__challenges__challenge_id__chunks_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"challenge_id","in":"path","required":true,"schema":{"type":"string","title":"Challenge Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_chunk_v1_sessions__session_id__challenges__challenge_id__chunks_post"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Upload Chunk V1 Sessions  Session Id  Challenges  Challenge Id  Chunks Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/challenges/{challenge_id}/submit":{"post":{"summary":"Submit Challenge","operationId":"submit_challenge_v1_sessions__session_id__challenges__challenge_id__submit_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"challenge_id","in":"path","required":true,"schema":{"type":"string","title":"Challenge Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitChallengeBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Submit Challenge V1 Sessions  Session Id  Challenges  Challenge Id  Submit Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/challenges/{challenge_id}/result":{"get":{"summary":"Get Challenge Result","operationId":"get_challenge_result_v1_sessions__session_id__challenges__challenge_id__result_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"challenge_id","in":"path","required":true,"schema":{"type":"string","title":"Challenge Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Challenge Result V1 Sessions  Session Id  Challenges  Challenge Id  Result Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/.well-known/trackorigin-public-key":{"get":{"summary":"Well Known Public Key Pem","description":"Public Ed25519 signing key in PEM format.\n\nThis is the key used to verify the `signature` field on TrackOrigin\ncertificate manifests. The key_id this corresponds to is published in the\nJSON variant at /.well-known/trackorigin-public-key.json and inside every\nissued certificate manifest under issuer.key_id.","operationId":"well_known_public_key_pem__well_known_trackorigin_public_key_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/trackorigin-public-key.json":{"get":{"summary":"Well Known Public Key Json","description":"Public signing key + metadata in JSON form.","operationId":"well_known_public_key_json__well_known_trackorigin_public_key_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/trackorigin-revocations.json":{"get":{"summary":"Well Known Revocations","description":"Account-less, signed revocation snapshot (Origin Seal §12.3).\n\nThe Origin Seal revocation poller fetches this on an interval and flips\nmatching bindings to `revoked`. The payload is built UNSIGNED, then its\nJCS-canonical bytes are Ed25519-signed and the signature attached — we never\nsign an object that contains its own signature. `sequence` is the monotonic\nhead (§13) so a consumer can detect rollback/replay; `generated_at` lets it\nenforce freshness and fail closed if the feed goes stale. Edge-cache ~5 min.\n\nVerify with the key at /.well-known/trackorigin-public-key.json:\n  ok = Ed25519_verify(pubkey, JCS(payload_without_signature), b64decode(signature))","operationId":"well_known_revocations__well_known_trackorigin_revocations_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/public/certificates/{cert_id}":{"get":{"summary":"Public Certificate","description":"Public certificate endpoint — no auth.\n\nReturns the manifest summary for active certs, or a revoked stub.\nPass ?download=1 to get the manifest as a downloadable JSON file.","operationId":"public_certificate_v1_public_certificates__cert_id__get","parameters":[{"name":"cert_id","in":"path","required":true,"schema":{"type":"string","title":"Cert Id"}},{"name":"download","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Download"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/public/artists/{handle}":{"get":{"summary":"Public Artist","description":"Public artist profile — no auth.\n\nReturns display info, profile fields (bio/location/links), and the list\nof active certificates (verified tracks).","operationId":"public_artist_v1_public_artists__handle__get","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string","title":"Handle"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Public Artist V1 Public Artists  Handle  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/contact":{"post":{"summary":"Submit Contact Form","description":"Public contact form — no auth.\n\nStores the submission in db.contacts for review.","operationId":"submit_contact_form_v1_contact_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactFormBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Submit Contact Form V1 Contact Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/submissions/{submission_id}/artifacts":{"get":{"summary":"List Submission Artifacts","description":"List every stored artifact for a submission — audit trail entry point.","operationId":"list_submission_artifacts_v1_submissions__submission_id__artifacts_get","parameters":[{"name":"submission_id","in":"path","required":true,"schema":{"type":"string","title":"Submission Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Submission Artifacts V1 Submissions  Submission Id  Artifacts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/storage/{storage_key}":{"get":{"summary":"Fetch Storage Artifact","description":"Serve a stored artifact by its storage key.\n\nAuth model: the storage_key must be REGISTERED on a submission owned by\nthe calling artist. We never trust the URL's structure for ownership —\na previous version derived the session_id from the URL parts, which let\nan attacker write `sessions/MINE/../VICTIM/foo.webm` and pass the\nownership check while resolving to a different session's file on disk.\n\nNow we look the key up against `submissions` (final-asset fields and\nthe artifacts[] array, since both can carry storage_keys), confirm the\nsubmission's parent session belongs to the caller, and only then resolve\nthe file. A path-traversal attempt that resolves to some other session's\nfile simply won't appear in any submission owned by the caller.","operationId":"fetch_storage_artifact_v1_storage__storage_key__get","parameters":[{"name":"storage_key","in":"path","required":true,"schema":{"type":"string","title":"Storage Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/methodology":{"get":{"summary":"Page Methodology","operationId":"page_methodology_methodology_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/contact":{"get":{"summary":"Page Contact","operationId":"page_contact_contact_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/terms":{"get":{"summary":"Page Terms","operationId":"page_terms_terms_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/privacy":{"get":{"summary":"Page Privacy","operationId":"page_privacy_privacy_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/license":{"get":{"summary":"Page License Summary","operationId":"page_license_summary_license_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/legal":{"get":{"summary":"Page Legal Index","operationId":"page_legal_index_legal_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/embed":{"get":{"summary":"Page Embed","operationId":"page_embed_embed_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/integrations":{"get":{"summary":"Page Integrations","description":"Dedicated marketing page for DSPs, labels, distributors and\nmarketplaces. Covers all three integration modes (read-only verify,\npartner-submit, white-label) in one place so volume partners can land\non a single URL and self-qualify before talking to sales.","operationId":"page_integrations_integrations_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/for-dsps":{"get":{"summary":"Page For Dsps","description":"Alias kept for inbound marketing links — same page as /integrations.","operationId":"page_for_dsps_for_dsps_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/u/{handle}":{"get":{"summary":"Page Artist Profile","operationId":"page_artist_profile_u__handle__get","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string","title":"Handle"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cert/{cert_id}":{"get":{"summary":"Page Certificate","operationId":"page_certificate_cert__cert_id__get","parameters":[{"name":"cert_id","in":"path","required":true,"schema":{"type":"string","title":"Cert Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/finalize":{"post":{"summary":"Finalize Session","operationId":"finalize_session_v1_sessions__session_id__finalize_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Finalize Session V1 Sessions  Session Id  Finalize Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/retry":{"post":{"summary":"Retry Failed Challenges","description":"Reset every failed challenge that still has a retry available, drop the\nsession back to READY so the lobby re-anchors face/media, and let the\nartist redo only those challenges in one pass before re-finalizing.\n\nEach challenge gets exactly one retry. After that, the artist must delete\nthe verification and upload the track again — enforced by refusing this\nendpoint when no retry-eligible challenges remain.","operationId":"retry_failed_challenges_v1_sessions__session_id__retry_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Retry Failed Challenges V1 Sessions  Session Id  Retry Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sessions/{session_id}/pause":{"post":{"summary":"Pause Session","description":"Pause an IN_PROGRESS session.\n\nSame code-path as retry — drop back to READY so the lobby re-anchors face\nand the studio replays only PENDING challenges — but without burning any\nretries (no challenge state is touched). Use when the artist needs to\nstep away and resume cleanly later.","operationId":"pause_session_v1_sessions__session_id__pause_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Pause Session V1 Sessions  Session Id  Pause Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/mode":{"get":{"summary":"Billing Mode","operationId":"billing_mode_v1_billing_mode_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Billing Mode V1 Billing Mode Get"}}}}}}},"/v1/billing/packages":{"get":{"summary":"Billing Packages","description":"Return packages localized to the caller's currency.\n\n`?country=XX` is an optional override (used when the user changes their\nregion from the pricing page). The server still validates the mapping\nand falls back to USD if the override is unsupported.","operationId":"billing_packages_v1_billing_packages_get","parameters":[{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Billing Packages V1 Billing Packages Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/balance":{"get":{"summary":"Billing Balance","operationId":"billing_balance_v1_billing_balance_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Billing Balance V1 Billing Balance Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/purchases":{"get":{"summary":"Billing Purchases","operationId":"billing_purchases_v1_billing_purchases_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Billing Purchases V1 Billing Purchases Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/purchases/by-session/{session_id}":{"get":{"summary":"Billing Purchase By Session","operationId":"billing_purchase_by_session_v1_billing_purchases_by_session__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Billing Purchase By Session V1 Billing Purchases By Session  Session Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/checkout":{"post":{"summary":"Billing Checkout","operationId":"billing_checkout_v1_billing_checkout_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_BillingCheckoutBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Billing Checkout V1 Billing Checkout Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/coupons/redeem":{"post":{"summary":"Billing Redeem Coupon","description":"Redeem a campaign coupon code and grant the configured credits.\n\nOne redemption per artist per coupon (enforced via a composite-key insert\ninto `coupon_redemptions`). The credit grant uses `_grant_credits` with a\ndeterministic idempotency key so a network-retried call doesn't double-\ngrant. Capacity is enforced atomically via a find_one_and_update with a\n`redeemed_count < max_redemptions` filter.","operationId":"billing_redeem_coupon_v1_billing_coupons_redeem_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_CouponRedeemBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Billing Redeem Coupon V1 Billing Coupons Redeem Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/logout":{"post":{"summary":"Admin Logout","description":"Invalidate the current admin token immediately.\n\nThe TTL'd `revoked_jtis` collection vacuums the row after the token\nwould have expired anyway, so the revocation list never grows unboundedly.","operationId":"admin_logout_v1_admin_logout_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Logout V1 Admin Logout Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/login":{"post":{"summary":"Admin Login","description":"Authenticate the admin operator and issue a Bearer token.\n\nRate-limited: 3 failed attempts (across IP OR persistent cookie\nfingerprint) trigger a 24-hour lockout. Successful login clears the\ncounters. The lockout is enforced server-side so clearing cookies\ndoesn't bypass it — the IP-based key still blocks.\n\nA long-lived `to_admin_fp` cookie is set on every visit so a returning\nbrowser is recognised even from a new IP, and a returning IP is\nrecognised even after cookie clears. Either being locked refuses login.","operationId":"admin_login_v1_admin_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AdminLoginBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Login V1 Admin Login Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/queue/stats":{"get":{"summary":"Admin Queue Stats","description":"Snapshot of the in-process ML job queue: depth, workers, in-flight\njobs (kind+id), and configured caps. Use to confirm backpressure is\nbehaving under load.","operationId":"admin_queue_stats_v1_admin_queue_stats_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Queue Stats V1 Admin Queue Stats Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/lifecycle-emails/run":{"post":{"summary":"Admin Run Lifecycle Emails","description":"Manually run one abandoned-session lifecycle pass. Lets you exercise the\ncadence/suppression logic (and actually send, if a SendGrid key is set)\nwithout enabling the background scheduler. Honours all suppression + opt-out\nrules exactly like the scheduled pass.","operationId":"admin_run_lifecycle_emails_v1_admin_lifecycle_emails_run_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Run Lifecycle Emails V1 Admin Lifecycle Emails Run Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/artists/lookup":{"get":{"summary":"Admin Lookup Artist","description":"Find an artist by email so the admin UI can confirm before granting.","operationId":"admin_lookup_artist_v1_admin_artists_lookup_get","parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string","title":"Email"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Lookup Artist V1 Admin Artists Lookup Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/certificates/{cert_id}/revoke":{"post":{"summary":"Admin Revoke Certificate","description":"Revoke a previously-issued certificate.\n\nSets `revoked_at` + `revoked_reason` + `revoked_by_admin` on the cert,\nflips its status to REVOKED, records an audit-log event, and fires a\nsigned `cert.revoked` webhook to the owning artist's subscribers.\n\nIdempotent: revoking an already-revoked cert returns the existing state\nwithout firing a second webhook. Reasonable when an admin clicks\nRevoke twice.","operationId":"admin_revoke_certificate_v1_admin_certificates__cert_id__revoke_post","parameters":[{"name":"cert_id","in":"path","required":true,"schema":{"type":"string","title":"Cert Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AdminRevokeCertBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Revoke Certificate V1 Admin Certificates  Cert Id  Revoke Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/origin-seal/backfill":{"post":{"summary":"Admin Origin Seal Backfill","description":"Backfill `audio_fp` over existing certs from their archived masters\n(Origin Seal §11). Establishes the fingerprint at the cert's CURRENT version\nand invalidates nothing — no binding can exist until audio_fp is present, so\nthis only ever enables sealing. Processes up to `limit` certs missing a\nfingerprint per call. Set `dual=true` to also compute the independent\n`audio_fp2` (high-risk tier). CPU-only; safe on the M1/no-GPU box.","operationId":"admin_origin_seal_backfill_v1_admin_origin_seal_backfill_post","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"dual","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dual"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Origin Seal Backfill V1 Admin Origin Seal Backfill Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/origin-seal/certificates/{cert_id}/high-risk":{"post":{"summary":"Admin Origin Seal Designate High Risk","description":"Designate (or clear) a certificate as high-risk → dual-fingerprint\nrequired (Origin Seal §10). On enable, generates the independent `audio_fp2`\nfrom the master if it is not already present and sets\n`requires_dual_fingerprint=true`. Existing single-fingerprint bindings then\nfail closed at render until BTR re-verifies with the second algorithm\n(that fail-closed behaviour lives in BTR's render gate). CPU-only.","operationId":"admin_origin_seal_designate_high_risk_v1_admin_origin_seal_certificates__cert_id__high_risk_post","parameters":[{"name":"cert_id","in":"path","required":true,"schema":{"type":"string","title":"Cert Id"}},{"name":"enable","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Enable"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Origin Seal Designate High Risk V1 Admin Origin Seal Certificates  Cert Id  High Risk Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/origin-seal/policies":{"get":{"summary":"Admin Origin Seal Policies","description":"List Origin Seal verification policies and the accepted-policy registry.","operationId":"admin_origin_seal_policies_v1_admin_origin_seal_policies_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Origin Seal Policies V1 Admin Origin Seal Policies Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/origin-seal/policies/{policy_id}/accept":{"post":{"summary":"Admin Origin Seal Accept Policy","description":"Accept a verification policy after §9.3 calibration: set its\n`fp_match_threshold` and flip `accepted=true`.\n\nGuards (§9.2): the threshold must be in (0, 1] — a 0.0 threshold (\"accept\neverything\") is forbidden, and null is never accepted. Once accepted, the\npolicy id enters the accepted-policy registry that render + attach + the\nrendition endpoint check; until then nothing can seal.","operationId":"admin_origin_seal_accept_policy_v1_admin_origin_seal_policies__policy_id__accept_post","parameters":[{"name":"policy_id","in":"path","required":true,"schema":{"type":"string","title":"Policy Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AcceptPolicyBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Origin Seal Accept Policy V1 Admin Origin Seal Policies  Policy Id  Accept Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/credits/grant":{"post":{"summary":"Admin Grant Credits","description":"Top up an artist's credit balance.\n\nEach grant gets a fresh non-deterministic ledger id (we *want* the admin\nto be able to top someone up multiple times for separate reasons), so we\ndeliberately do NOT pass an idempotency_key. The UI confirms before\nsubmitting to keep a double-click from minting two grants by accident.","operationId":"admin_grant_credits_v1_admin_credits_grant_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AdminGrantCreditsBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Grant Credits V1 Admin Credits Grant Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/coupons/create":{"post":{"summary":"Admin Create Coupon","description":"Mint a campaign coupon. Code is normalised to uppercase.","operationId":"admin_create_coupon_v1_admin_coupons_create_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AdminCreateCouponBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Create Coupon V1 Admin Coupons Create Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/dashboard":{"get":{"summary":"Admin Dashboard","description":"All operational metrics for the admin dashboard in a single response.\n\nTime-windowed: the `window` parameter scopes anything labelled `_window`,\nwhile all-time totals live in dedicated `total_*` fields. Aggregations\nare run in parallel via asyncio.gather where possible.","operationId":"admin_dashboard_v1_admin_dashboard_get","parameters":[{"name":"window","in":"query","required":false,"schema":{"enum":["24h","7d","30d","90d","all"],"type":"string","default":"7d","title":"Window"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Dashboard V1 Admin Dashboard Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/certificates/{cert_id}":{"get":{"summary":"Get Certificate","operationId":"get_certificate_v1_certificates__cert_id__get","parameters":[{"name":"cert_id","in":"path","required":true,"schema":{"type":"string","title":"Cert Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Certificate V1 Certificates  Cert Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Page Home","operationId":"page_home__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/signup":{"get":{"summary":"Page Signup","operationId":"page_signup_signup_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/login":{"get":{"summary":"Page Login","operationId":"page_login_login_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/forgot-password":{"get":{"summary":"Page Forgot Password","operationId":"page_forgot_password_forgot_password_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/reset-password":{"get":{"summary":"Page Reset Password","operationId":"page_reset_password_reset_password_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/why":{"get":{"summary":"Why","operationId":"why_why_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/dashboard":{"get":{"summary":"Page Dashboard","operationId":"page_dashboard_dashboard_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/account":{"get":{"summary":"Page Account","operationId":"page_account_account_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api-keys":{"get":{"summary":"Page Api Keys","description":"Standalone API keys + webhooks management (moved out of /account).","operationId":"page_api_keys_api_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/website-integration":{"get":{"summary":"Page Website Integration","description":"Idiot-proof copy-paste guide for adding the seal to any website.","operationId":"page_website_integration_website_integration_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/platform-integration":{"get":{"summary":"Page Platform Integration","description":"Step-by-step integration guide for music platforms / DSPs.","operationId":"page_platform_integration_platform_integration_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/verify":{"get":{"summary":"Page Verify","operationId":"page_verify_verify_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/standard":{"get":{"summary":"Standard","operationId":"standard_standard_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/upload":{"get":{"summary":"Page Upload","operationId":"page_upload_upload_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/declaration":{"get":{"summary":"Page Declaration","operationId":"page_declaration_declaration_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/pricing":{"get":{"summary":"Page Pricing","operationId":"page_pricing_pricing_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/billing":{"get":{"summary":"Page Billing","operationId":"page_billing_billing_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/checkout/success":{"get":{"summary":"Page Checkout Success","operationId":"page_checkout_success_checkout_success_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/resolve":{"get":{"summary":"Page Resolve","operationId":"page_resolve_resolve_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/studio/{session_id}":{"get":{"summary":"Page Studio","description":"Render the verification studio.\n\nWhen the request includes `?embed=1` and the session's owning partner\nhas a configured `frame_ancestors` list, we set a CSP header that\nallows the studio to be iframed from those origins (and only those).\nThis is what makes the white-label \"host the studio under your brand\"\nflow work — the partner serves an iframe pointing at us, with their\ndomain whitelisted server-side via the partner-key.\n\nWithout `?embed=1` (the default), we keep the strict same-origin\nframe-ancestors so the studio can't be iframed by random sites.","operationId":"page_studio_studio__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/scoring/{session_id}":{"get":{"summary":"Page Scoring","operationId":"page_scoring_scoring__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/result/{session_id}":{"get":{"summary":"Page Result","operationId":"page_result_result__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/certificates/changes":{"get":{"tags":["certificates"],"summary":"Api List Changes","description":"Combined issuance + revocation feed.\n\nTwo modes:\n\n• **Sequence mode (Origin Seal §12.5, preferred)** — pass\n  `?since_sequence=N`. Returns the monotonically-sequenced revocation/change\n  events with `sequence > N`, plus the current `sequence` head. BTR's\n  revocation poller stores `last_applied_sequence` and rejects any response\n  whose head is `<= last_applied` (unless identical), so a rollback or\n  replay can never un-revoke a cert. Timestamps alone are not trusted.\n\n• **Timestamp mode (legacy)** — pass `?since=<ISO-8601>` (or nothing, →\n  last 24h). Returns the combined issued+revoked cert feed. Still carries\n  the current `sequence` head so callers can migrate.","operationId":"api_list_changes_api_v1_certificates_changes_get","parameters":[{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Since"}},{"name":"since_sequence","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Since Sequence"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api List Changes Api V1 Certificates Changes Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/certificates/{cert_id}":{"get":{"tags":["certificates"],"summary":"Api Get Certificate","description":"Fetch a certificate's full signed manifest. Public.\n\nReturns the same canonical body that was signed plus the base64 Ed25519\nsignature. Verify independently against /.well-known/trackorigin-public-key\n— see /verify for code samples.\n\nSandbox: any `cer_test_…` id resolves from the sandbox store first\n(seeded via `POST /api/v1/sandbox/certificates`) with the built-in\nsynthetic available as `cer_test_synthetic` to anonymous callers too.","operationId":"api_get_certificate_api_v1_certificates__cert_id__get","parameters":[{"name":"cert_id","in":"path","required":true,"schema":{"type":"string","title":"Cert Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Get Certificate Api V1 Certificates  Cert Id  Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/certificates/{cert_id}/status":{"get":{"tags":["certificates"],"summary":"Api Get Certificate Status","description":"Lightweight liveness check for a certificate.\n\nDesigned for embed widgets and DSP integrations that poll often: returns\nonly the fields needed to render a live badge, no manifest body.\n\nOrigin Seal (§12.4): pass `?no_cache=true` for the FRESH pre-write cert\nre-check in the attach flow. With no_cache the response is served\n`Cache-Control: no-store` so a CDN/edge layer can never hand back a stale\n\"active\" for a cert that was just revoked. The response carries the trust\nfields BTR needs: `status`, `version`, `checked_at`.","operationId":"api_get_certificate_status_api_v1_certificates__cert_id__status_get","parameters":[{"name":"cert_id","in":"path","required":true,"schema":{"type":"string","title":"Cert Id"}},{"name":"no_cache","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"No Cache"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Get Certificate Status Api V1 Certificates  Cert Id  Status Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tracks/by-sha256/{sha256_hex}":{"get":{"tags":["certificates"],"summary":"Api Get Certificate By Audio Hash","description":"Reverse-lookup: is this exact audio file verified?\n\nCompute the SHA-256 of the audio bytes on your side, pass the hex digest\nhere. Resolves via BOTH master uploads and registered off-platform\nrenditions (Origin Seal §12.6), so a content-addressed transcode SHA that a\nDSP serves can resolve to its cert. Useful for deciding whether to render an\nOrigin Seal next to a track row without coordinating with the artist.\n\nResolution rule (§12.6):\n    collect active cert candidates from masters + renditions\n    dedupe by cert_id, drop revoked\n    0  → 404\n    1  → return the cert\n    >1 → 409 ambiguous_multiple_certificates  (resolve nothing)","operationId":"api_get_certificate_by_audio_hash_api_v1_tracks_by_sha256__sha256_hex__get","parameters":[{"name":"sha256_hex","in":"path","required":true,"schema":{"type":"string","title":"Sha256 Hex"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Get Certificate By Audio Hash Api V1 Tracks By Sha256  Sha256 Hex  Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/certificates/{cert_id}/verify-audio":{"post":{"tags":["certificates"],"summary":"Api Verify Audio","description":"Verify ONE artifact fingerprint against ONE certificate (Origin Seal §12.1).\n\nRead-only — mutates no cert and no rendition. Public but rate-limited\n(lower for anonymous, higher with an API key). Public callers get a coarse\n`confidence` band and no exact `score`; authenticated BTR gets the exact\nscore and the full receipt to store in its binding.\n\n• `422 fingerprint_unavailable` when the cert has no `audio_fp` yet\n  (issuance failed / awaiting backfill) — caller surfaces \"temporarily\n  unavailable\" and never seals.\n• Enforces the second, independent fingerprint when the cert is high-risk\n  (`requires_dual_fingerprint`).\n• `matched` can be true ONLY under a calibrated, accepted verification\n  policy — an uncalibrated policy always returns `matched:false`,\n  `confidence:\"uncalibrated\"`, so nothing seals before calibration (§9.2).","operationId":"api_verify_audio_api_v1_certificates__cert_id__verify_audio_post","parameters":[{"name":"cert_id","in":"path","required":true,"schema":{"type":"string","title":"Cert Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyAudioBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Verify Audio Api V1 Certificates  Cert Id  Verify Audio Post"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/renditions":{"post":{"tags":["certificates"],"summary":"Api Register Rendition","description":"Register an off-platform artifact (a transcode a DSP/distributor serves)\nso `by-sha256` can resolve its content hash to the cert (Origin Seal §12.2).\n\nv1 = trusted-integrator allowlist (Option C). Only allowlisted service\naccounts (e.g. BTR) may register; public/unlisted keys get 403 (AC 50).\nTrackOrigin VERIFIES the supplied fingerprint against the cert master; the\nintegrator attests the `sha256` and fingerprint came from the same bytes\n(AC 51). The cert's active status is re-checked fresh here. A high-risk cert\nrequires both fingerprints to pass. Nothing is registered unless the\nfingerprint matches under a calibrated, accepted policy.","operationId":"api_register_rendition_api_v1_renditions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenditionBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Register Rendition Api V1 Renditions Post"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/artists/{handle}":{"get":{"tags":["artists"],"summary":"Api Get Artist","description":"Public artist profile by handle.","operationId":"api_get_artist_api_v1_artists__handle__get","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string","title":"Handle"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Get Artist Api V1 Artists  Handle  Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/artists/{handle}/certificates":{"get":{"tags":["artists"],"summary":"Api List Artist Certificates","description":"Paginated list of an artist's certificates, newest first.\n\nCursor is opaque — pass back whatever `next_cursor` we return.","operationId":"api_list_artist_certificates_api_v1_artists__handle__certificates_get","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string","title":"Handle"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api List Artist Certificates Api V1 Artists  Handle  Certificates Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/certificates/bulk":{"post":{"tags":["certificates"],"summary":"Api Bulk Certificates","description":"Batch lookup of up to 100 certificate IDs in a single request.\n\nReturns an object keyed by cert_id. Missing IDs map to null.\nAuthenticated only (pk_ or sk_) — protects the DB from unauthenticated\nfan-out abuse.","operationId":"api_bulk_certificates_api_v1_certificates_bulk_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkCertBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Bulk Certificates Api V1 Certificates Bulk Post"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me":{"get":{"tags":["account"],"summary":"Api Me","description":"Whoami — sanity check that your secret key is valid + identifies the\nartist account it's tied to. Useful for partner integration smoke tests.","operationId":"api_me_api_v1_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Me Api V1 Me Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."}}}},"/v1/me/api-keys":{"get":{"summary":"List My Api Keys","description":"List the current artist's API keys (no raw key material; ever).","operationId":"list_my_api_keys_v1_me_api_keys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List My Api Keys V1 Me Api Keys Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"summary":"Create My Api Key","description":"Mint a new API key. The full key value is returned ONCE — store it\nsomewhere safe. We only persist its sha256 so a DB leak yields nothing\nusable. Lost keys can't be recovered; you have to revoke and remint.","operationId":"create_my_api_key_v1_me_api_keys_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIKeyCreateBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Create My Api Key V1 Me Api Keys Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/api-usage":{"get":{"summary":"My Api Usage","description":"Current month's API call breakdown: free / paid / remaining quota /\nbalance. Surfaced in /account and used by the developer docs page to\nshow live usage.","operationId":"my_api_usage_v1_me_api_usage_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response My Api Usage V1 Me Api Usage Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/api-keys/{key_id}":{"delete":{"summary":"Revoke My Api Key","description":"Revoke a key. Auth on /api/v1 with that key starts failing immediately.","operationId":"revoke_my_api_key_v1_me_api_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Revoke My Api Key V1 Me Api Keys  Key Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/api-keys/{key_id}/frame-ancestors":{"put":{"summary":"Update Api Key Frame Ancestors","description":"Set the iframe-embed origin whitelist for one of your API keys.\n\nEach origin must be `https://host[:port]` — paths and wildcards are\nrejected. The studio honours this list when serving `/studio/{session}?embed=1`\nfor a session initiated by this key. Empty list disables embedding.","operationId":"update_api_key_frame_ancestors_v1_me_api_keys__key_id__frame_ancestors_put","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIKeyFrameAncestorsBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Update Api Key Frame Ancestors V1 Me Api Keys  Key Id  Frame Ancestors Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/api-summary":{"get":{"summary":"Admin Api Summary","description":"Aggregated developer-API analytics for the admin console.\n\nReturns:\n  - Roll-up totals for the window (requests, success/4xx/5xx, p50/p95 latency proxy)\n  - Per-day series (count of requests, by day) for the window\n  - Top endpoints by request volume\n  - Top callers (by key_id or IP) by request volume\n  - All API keys ever issued (with owner, status, last used, rate limit)\n  - Recent key-related audit events","operationId":"admin_api_summary_v1_admin_api_summary_get","parameters":[{"name":"window","in":"query","required":false,"schema":{"enum":["24h","7d","30d","90d"],"type":"string","default":"30d","title":"Window"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Api Summary V1 Admin Api Summary Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/webhooks":{"post":{"summary":"Create My Webhook","description":"Create a webhook subscription. Secret returned in response is the only\nchance to copy it — use it on the receiver to verify HMAC signatures.\n\nScope handling: `scope=\"artist\"` (the default) is open to any logged-in\nartist. `scope=\"global\"` — for DSPs / catalogue partners who want every\nplatform-wide event — requires authentication via a secret API key (or\na `partner_program=True` flag set by an admin).","operationId":"create_my_webhook_v1_me_webhooks_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreateBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Create My Webhook V1 Me Webhooks Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"List My Webhooks","operationId":"list_my_webhooks_v1_me_webhooks_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List My Webhooks V1 Me Webhooks Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/webhooks/{webhook_id}":{"delete":{"summary":"Delete My Webhook","operationId":"delete_my_webhook_v1_me_webhooks__webhook_id__delete","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Delete My Webhook V1 Me Webhooks  Webhook Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/webhooks/{webhook_id}/rotate-secret":{"post":{"summary":"Rotate My Webhook Secret","description":"Rotate the HMAC secret for a webhook subscription.\n\nReturns the new secret in the response — shown ONCE — for the artist to\npaste into their receiver. Any in-flight deliveries already signed with\nthe old secret will fail on the receiver side; the artist should switch\nover to verifying with the new secret immediately. Audit-logged.","operationId":"rotate_my_webhook_secret_v1_me_webhooks__webhook_id__rotate_secret_post","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Rotate My Webhook Secret V1 Me Webhooks  Webhook Id  Rotate Secret Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/webhooks/{webhook_id}/test":{"post":{"summary":"Test My Webhook","description":"Fire a synthetic `cert.issued` event to the subscription URL. Useful\nfor confirming the receiver verifies signatures and parses the envelope\ncorrectly before going live.","operationId":"test_my_webhook_v1_me_webhooks__webhook_id__test_post","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Test My Webhook V1 Me Webhooks  Webhook Id  Test Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/webhooks/{webhook_id}/deliveries":{"get":{"summary":"List My Webhook Deliveries","description":"Recent delivery attempts (newest first), up to `limit` (max 100).","operationId":"list_my_webhook_deliveries_v1_me_webhooks__webhook_id__deliveries_get","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List My Webhook Deliveries V1 Me Webhooks  Webhook Id  Deliveries Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/webhooks/verification-spec":{"get":{"tags":["webhooks"],"summary":"Api Webhook Verification Spec","description":"How to verify a TrackOrigin webhook.\n\nReturns the signing algorithm, header layout, replay-window tolerance,\ncanonical-body rules and copy-pasteable receiver snippets in Python and\nNode. A partner integration can render this directly into their internal\ndocs or run their unit tests against it.\n\nEach delivery includes:\n\n  `TO-Signature: t=<unix_ts>,v1=<hex_hmac>` — HMAC-SHA256(secret, \"{t}.{raw_body}\")\n  `TO-Timestamp: <unix_ts>`                 — same `t` value, surfaced for clock-skew checks\n  `TO-Event: cert.issued`                   — the event type\n  `TO-Event-Id: evt_…`                      — stable per event; dedupe key for receivers\n  `TO-Delivery-Id: whd_…`                   — unique per retry attempt\n\nReceivers should:\n  1. Read the raw bytes BEFORE JSON-parsing the body.\n  2. Reject if `abs(now - t) > 300s` (replay protection).\n  3. Recompute the HMAC and `hmac.compare_digest` against `v1`.\n  4. Dedupe on `TO-Event-Id`; the same id may arrive multiple times after\n     a 5xx retry — that's a feature, not a bug.","operationId":"api_webhook_verification_spec_api_v1_webhooks_verification_spec_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Webhook Verification Spec Api V1 Webhooks Verification Spec Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."}}}},"/api/v1/sandbox/certificates":{"post":{"tags":["sandbox"],"summary":"Sandbox Seed Certificate","description":"Seed a custom sandbox certificate.\n\nLets a partner build a deterministic fixture for their integration tests\n— pin a verdict, ISRC, UPC, artist handle, etc. The seeded doc is only\nreadable by sandbox-aware endpoints; it never appears in live results.\n\nIdempotent on `cert_id`: posting with the same id replaces the previous\nseeded version. Each call returns the cert in the same public shape as\n`GET /api/v1/certificates/{cert_id}`.","operationId":"sandbox_seed_certificate_api_v1_sandbox_certificates_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxSeedCertBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Sandbox Seed Certificate Api V1 Sandbox Certificates Post"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["sandbox"],"summary":"Sandbox List Certificates","description":"List sandbox certificates seeded by the calling key. The built-in\nsynthetic is always appended last so partners always have one fixture\nto read against.","operationId":"sandbox_list_certificates_api_v1_sandbox_certificates_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Sandbox List Certificates Api V1 Sandbox Certificates Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/sandbox/certificates/{cert_id}":{"delete":{"tags":["sandbox"],"summary":"Sandbox Delete Certificate","description":"Remove a previously-seeded sandbox cert. Idempotent.","operationId":"sandbox_delete_certificate_api_v1_sandbox_certificates__cert_id__delete","parameters":[{"name":"cert_id","in":"path","required":true,"schema":{"type":"string","title":"Cert Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Sandbox Delete Certificate Api V1 Sandbox Certificates  Cert Id  Delete"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/certificates/by-isrc/{isrc}":{"get":{"tags":["certificates"],"summary":"Api Get Certificate By Isrc","description":"Lookup a certificate by ISRC. Returns the most-recently-issued cert\nwhose master.isrc matches — useful when a track was re-uploaded but\nkeeps the same ISRC.","operationId":"api_get_certificate_by_isrc_api_v1_certificates_by_isrc__isrc__get","parameters":[{"name":"isrc","in":"path","required":true,"schema":{"type":"string","title":"Isrc"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Get Certificate By Isrc Api V1 Certificates By Isrc  Isrc  Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/certificates/by-upc/{upc}":{"get":{"tags":["certificates"],"summary":"Api Get Certificate By Upc","description":"Lookup ALL certificates for a UPC (a release can contain many tracks).","operationId":"api_get_certificate_by_upc_api_v1_certificates_by_upc__upc__get","parameters":[{"name":"upc","in":"path","required":true,"schema":{"type":"string","title":"Upc"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Get Certificate By Upc Api V1 Certificates By Upc  Upc  Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/certificates/by-iswc/{iswc}":{"get":{"tags":["certificates"],"summary":"Api Get Certificate By Iswc","description":"Lookup all certificates for an ISWC (composition identifier; one\ncomposition can have many recordings).","operationId":"api_get_certificate_by_iswc_api_v1_certificates_by_iswc__iswc__get","parameters":[{"name":"iswc","in":"path","required":true,"schema":{"type":"string","title":"Iswc"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Get Certificate By Iswc Api V1 Certificates By Iswc  Iswc  Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/certificates/by-isrc/bulk":{"post":{"tags":["certificates"],"summary":"Api Bulk By Isrc","description":"Batch ISRC → certificate resolution for up to 1,000 ISRCs.\n\nReturns an object keyed by the *normalised* ISRC. Missing ones map to\nnull. Authenticated only — this is the catalogue-sync endpoint.","operationId":"api_bulk_by_isrc_api_v1_certificates_by_isrc_bulk_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkByISRCBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Bulk By Isrc Api V1 Certificates By Isrc Bulk Post"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/certificates":{"get":{"tags":["certificates"],"summary":"Api List Certificates","description":"Paginated, time-windowed list of all certificates.\n\nAuthenticated only — the unauth public surface is per-artist via\n`/artists/{handle}/certificates`. This endpoint is for catalogue-wide\nsync by DSPs / labels.\n\nStable cursor: `issued_at` descending. Pass `since=<iso>` for the\nfirst page, then `cursor=<next_cursor>` for subsequent pages.","operationId":"api_list_certificates_api_v1_certificates_get","parameters":[{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Since"}},{"name":"until","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Until"}},{"name":"verdict","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Verdict"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api List Certificates Api V1 Certificates Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/exports/certificates":{"get":{"tags":["exports"],"summary":"Api Export Certificates","description":"Compliance / royalty / auditor export of certificates.\n\nSupports CSV (default — opens in Excel) and JSON. All filters\noptional. Columns: cert_id, isrc, upc, iswc, sha256, verdict, revoked,\nartist_handle, artist_display_name, track_title, ai_tools_used,\nissued_at, revoked_at, revoked_reason.\n\nHard cap of 10,000 rows per call. Use `since` to page through large\ncatalogues — combined with `/certificates` for cursor pagination if\nyou need everything.","operationId":"api_export_certificates_api_v1_exports_certificates_get","parameters":[{"name":"format","in":"query","required":false,"schema":{"enum":["json","csv"],"type":"string","default":"csv","title":"Format"}},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Since"}},{"name":"until","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Until"}},{"name":"verdict","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Verdict"}},{"name":"handle","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle"}},{"name":"isrc","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Isrc"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10000,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/.well-known/trackorigin-public-keys.json":{"get":{"summary":"Well Known All Keys","description":"All known signing keys (active + retired). Verifiers should use the\nmanifest's `signing_key_id` field to pick the right one.\n\nThis complements `/.well-known/trackorigin-public-key.json` which\nreturns only the current key.","operationId":"well_known_all_keys__well_known_trackorigin_public_keys_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Well Known All Keys  Well Known Trackorigin Public Keys Json Get"}}}}}}},"/v1/admin/signing-keys/rotate":{"post":{"summary":"Admin Rotate Signing Key","description":"Register a new signing key and retire the current one in the registry.\n\nNOTE: this only updates the DB registry. The actual rotation requires:\n  1. Generate a new Ed25519 keypair offline\n  2. Place the new private key at settings.signing_private_key_path\n  3. Update settings.signing_key_id to new_kid\n  4. Call this endpoint with new_kid + new_public_key_pem\n  5. Restart the app\n\nFuture certificates will be signed with the new key. Existing certs\nremain verifiable because the retired key's PEM is still served from\n/.well-known/trackorigin-public-keys.json keyed by its old kid.","operationId":"admin_rotate_signing_key_v1_admin_signing_keys_rotate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AdminRotateKeyBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Rotate Signing Key V1 Admin Signing Keys Rotate Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/transparency/log":{"get":{"tags":["transparency"],"summary":"Api Transparency Log","description":"Paginated read of the Certificate Transparency log.\n\nEach entry: seq_no (1-indexed) · cert_id · manifest_hash · prev_hash ·\nentry_hash · ts. Hash-chained: entry N's prev_hash equals entry N-1's\nentry_hash, so deletion of any entry is detectable by re-verifying the\nchain.\n\nFor a signed tree-head snapshot (so monitors can poll a single\nsummary), see /transparency/sth.","operationId":"api_transparency_log_api_v1_transparency_log_get","parameters":[{"name":"since","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Since"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":1000,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Transparency Log Api V1 Transparency Log Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/transparency/sth":{"get":{"tags":["transparency"],"summary":"Api Transparency Sth","description":"Signed Tree Head — a compact, signed summary of the CT log state.\n\nIncludes the latest seq_no, the latest entry_hash (head of the hash\nchain), and an Ed25519 signature over them with our signing key. A\nthird-party monitor can pin one STH and detect any later state where\nthe chain doesn't extend cleanly from this head.","operationId":"api_transparency_sth_api_v1_transparency_sth_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Transparency Sth Api V1 Transparency Sth Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."}}}},"/api/v1/certificates/{cert_id}/c2pa":{"get":{"tags":["certificates"],"summary":"Api Get Certificate C2Pa","description":"C2PA-compliant manifest for the given certificate.\n\nMaps our internal signed manifest into a C2PA \"manifest store\" with\ntyped assertions covering content authorship and AI-tool usage.\nUse this for partners adopting the C2PA standard or for EU AI Act\ncompliance disclosure.","operationId":"api_get_certificate_c2pa_api_v1_certificates__cert_id__c2pa_get","parameters":[{"name":"cert_id","in":"path","required":true,"schema":{"type":"string","title":"Cert Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Api Get Certificate C2Pa Api V1 Certificates  Cert Id  C2Pa Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/organisations":{"post":{"summary":"Admin Create Organisation","description":"Create a partner organisation. Admin-only — partners onboard by\ncontract today, so creation is a controlled admin action. The org's\nfirst member must be added separately via /members.","operationId":"admin_create_organisation_v1_admin_organisations_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgCreateBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Create Organisation V1 Admin Organisations Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/organisations/{org_id}/members":{"post":{"summary":"Admin Add Org Member","operationId":"admin_add_org_member_v1_admin_organisations__org_id__members_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMemberAddBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Add Org Member V1 Admin Organisations  Org Id  Members Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/oauth/apps":{"post":{"summary":"Admin Register Oauth App","description":"Register a new OAuth client. Admin-only — partners apply through\nsales today. Returns `client_id` + `client_secret` exactly once.","operationId":"admin_register_oauth_app_v1_admin_oauth_apps_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthAppRegisterBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Register Oauth App V1 Admin Oauth Apps Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/oauth/authorize":{"get":{"summary":"Oauth Authorize","description":"OAuth authorize endpoint. Validates client + redirect_uri, ensures the\nuser is logged in (bouncing through /login if not), then renders the\nconsent screen. POSTing to /oauth/authorize/decide finalises the grant.","operationId":"oauth_authorize_oauth_authorize_get","parameters":[{"name":"client_id","in":"query","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string","title":"Redirect Uri"}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","default":"code","title":"Response Type"}},{"name":"scope","in":"query","required":false,"schema":{"type":"string","default":"read:profile","title":"Scope"}},{"name":"state","in":"query","required":false,"schema":{"type":"string","default":"","title":"State"}},{"name":"code_challenge","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Challenge"}},{"name":"code_challenge_method","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Challenge Method"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/oauth/authorize/decide":{"post":{"summary":"Oauth Authorize Decide","description":"Finalise the consent. On Allow we mint a one-time auth code, store\nits hash + scopes + artist_id, and redirect back to the partner with\n?code=…&state=…","operationId":"oauth_authorize_decide_oauth_authorize_decide_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_oauth_authorize_decide_oauth_authorize_decide_post"}}},"required":true},"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/oauth/token":{"post":{"summary":"Oauth Token","description":"Exchange a one-time auth code for an access token.\n\nClient authentication: HTTP Basic with `client_id:client_secret` is\nthe standard form. We also accept `client_id` + `client_secret` as\nform fields for clients (e.g. some mobile SDKs) that can't set\nthe Basic header cleanly. Token is a random opaque string — stored\nas SHA-256 hash, expires in 24h.","operationId":"oauth_token_oauth_token_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_oauth_token_oauth_token_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Oauth Token Oauth Token Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/me":{"get":{"tags":["partner"],"summary":"Oauth Me","description":"OAuth resource endpoint: profile data. Requires `read:profile`.","operationId":"oauth_me_api_v1_oauth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Oauth Me Api V1 Oauth Me Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."}}}},"/api/v1/oauth/me/certificates":{"get":{"tags":["partner"],"summary":"Oauth My Certificates","description":"OAuth resource endpoint: the user's certificates. Requires\n`read:certificates`. Returns full signed manifests so the partner\ncan verify offline.","operationId":"oauth_my_certificates_api_v1_oauth_me_certificates_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Oauth My Certificates Api V1 Oauth Me Certificates Get"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/oidc/issuers":{"post":{"summary":"Admin Register Oidc Issuer","description":"Register a trusted external OIDC issuer. Required before partner\nSSO calls to /v1/auth/oidc-bridge will accept tokens from that IdP.","operationId":"admin_register_oidc_issuer_v1_admin_oidc_issuers_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OIDCIssuerRegisterBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Admin Register Oidc Issuer V1 Admin Oidc Issuers Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/oidc-bridge":{"post":{"summary":"Oidc Bridge Login","description":"Exchange a partner-IdP ID token for a TrackOrigin session.\n\nThe partner's portal (after the user has already authenticated against\ntheir own IdP) POSTs the OIDC ID token here. We verify it against the\npre-registered JWKS for that issuer, find or auto-create the artist\nby email, and mint our access + refresh tokens.\n\nAuto-creation requires the ID token to carry an `email_verified=true`\nclaim. Without it, we refuse rather than risk binding an unverified\naddress to a new account (which would let a malicious portal claim\n\"user@unrelated.com\" without owning it).","operationId":"oidc_bridge_login_v1_auth_oidc_bridge_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OIDCBridgeBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Oidc Bridge Login V1 Auth Oidc Bridge Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partner/sessions":{"post":{"tags":["partner"],"summary":"Partner Create Session","description":"Initiate a verification session on behalf of a contracted artist.\n\nRequires:\n  - Secret API key (sk_) so only server-side partner integrations can call\n  - A consent_token JWT signed by the artist, scoping the partner +\n    allowed actions + expiry\n\nThe actual live verification still has to be completed by the artist\non their own device — this endpoint only PRE-CREATES the session so\nthe partner can send the artist a link / SMS / email to complete it.\n\n**Idempotency:** pass an `Idempotency-Key` header (any ≤128-char string)\nto make this POST safely retryable. A second call with the same key from\nthe same API key replays the original response with `Idempotent-Replay: true`.","operationId":"partner_create_session_api_v1_partner_sessions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerSessionCreateBody"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Partner Create Session Api V1 Partner Sessions Post"}}}},"400":{"description":"Malformed input (bad ID format, invalid params)."},"401":{"description":"Missing or invalid API key on a route that requires one."},"402":{"description":"Insufficient credits — monthly free API quota exhausted and credit balance is 0. Top up at /pricing."},"403":{"description":"Publishable key used on a secret-only endpoint."},"404":{"description":"Resource not found."},"429":{"description":"Rate limit exceeded. Honour the Retry-After header."},"500":{"description":"Internal server error. Safe to retry with backoff."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/developers":{"get":{"summary":"Page Developers","description":"Public developer documentation hub.","operationId":"page_developers_developers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AIToolUsage":{"type":"string","enum":["none","ai_mastering","ai_mixing_assist","ai_pitch_correction","ai_stem_separation","ai_lyrics_assist","ai_melody_suggestion","ai_vocal_generation","ai_full_generation"],"title":"AIToolUsage"},"APIKeyCreateBody":{"properties":{"name":{"type":"string","maxLength":64,"minLength":1,"title":"Name"},"type":{"type":"string","enum":["publishable","secret"],"title":"Type"}},"additionalProperties":false,"type":"object","required":["name","type"],"title":"APIKeyCreateBody"},"APIKeyFrameAncestorsBody":{"properties":{"frame_ancestors":{"items":{"type":"string"},"type":"array","maxItems":10,"title":"Frame Ancestors"}},"additionalProperties":false,"type":"object","title":"APIKeyFrameAncestorsBody","description":"Whitelist of `https://` origins allowed to iframe-embed the studio\nwhen a session was initiated via this API key. Required for the\nwhite-label hosted-studio flow — without it the CSP header refuses to\nlet the partner's parent frame render us."},"ArtistRole":{"type":"string","enum":["wrote_music","wrote_lyrics","produced","lead_vocals","backing_vocals","played_instruments","programmed_beats","sound_design","recorded_engineered","mixed","mastered"],"title":"ArtistRole"},"Body_oauth_authorize_decide_oauth_authorize_decide_post":{"properties":{"client_id":{"type":"string","title":"Client Id"},"redirect_uri":{"type":"string","title":"Redirect Uri"},"state":{"type":"string","title":"State","default":""},"scope":{"type":"string","title":"Scope","default":"read:profile"},"decision":{"type":"string","title":"Decision"},"code_challenge":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Challenge"}},"type":"object","required":["client_id","redirect_uri","decision"],"title":"Body_oauth_authorize_decide_oauth_authorize_decide_post"},"Body_oauth_token_oauth_token_post":{"properties":{"grant_type":{"type":"string","title":"Grant Type"},"code":{"type":"string","title":"Code","default":""},"redirect_uri":{"type":"string","title":"Redirect Uri","default":""},"code_verifier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Verifier"}},"type":"object","required":["grant_type"],"title":"Body_oauth_token_oauth_token_post"},"Body_set_face_anchor_v1_sessions__session_id__face_anchor_post":{"properties":{"frames":{"items":{"type":"string","format":"binary"},"type":"array","title":"Frames"}},"type":"object","required":["frames"],"title":"Body_set_face_anchor_v1_sessions__session_id__face_anchor_post"},"Body_upload_avatar_v1_me_avatar_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_avatar_v1_me_avatar_post"},"Body_upload_chunk_v1_sessions__session_id__challenges__challenge_id__chunks_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"sequence":{"type":"integer","title":"Sequence"},"sha256":{"type":"string","title":"Sha256"},"client_chain_hash":{"type":"string","title":"Client Chain Hash"},"captured_at":{"type":"string","title":"Captured At"},"kind":{"type":"string","title":"Kind"}},"type":"object","required":["file","sequence","sha256","client_chain_hash","captured_at","kind"],"title":"Body_upload_chunk_v1_sessions__session_id__challenges__challenge_id__chunks_post"},"Body_upload_track_v1_tracks_post":{"properties":{"title":{"type":"string","maxLength":200,"minLength":1,"title":"Title"},"file":{"type":"string","format":"binary","title":"File"},"isrc":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Isrc"},"upc":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Upc"},"iswc":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Iswc"}},"type":"object","required":["title","file"],"title":"Body_upload_track_v1_tracks_post"},"BulkByISRCBody":{"properties":{"isrcs":{"items":{"type":"string"},"type":"array","maxItems":1000,"minItems":1,"title":"Isrcs"}},"additionalProperties":false,"type":"object","required":["isrcs"],"title":"BulkByISRCBody"},"BulkCertBody":{"properties":{"cert_ids":{"items":{"type":"string"},"type":"array","maxItems":100,"minItems":1,"title":"Cert Ids"}},"additionalProperties":false,"type":"object","required":["cert_ids"],"title":"BulkCertBody"},"CollaboratorCredit":{"properties":{"name":{"type":"string","maxLength":128,"minLength":1,"title":"Name"},"role":{"type":"string","maxLength":128,"minLength":1,"title":"Role"}},"additionalProperties":false,"type":"object","required":["name","role"],"title":"CollaboratorCredit"},"ContactFormBody":{"properties":{"name":{"type":"string","maxLength":128,"minLength":1,"title":"Name"},"email":{"type":"string","format":"email","title":"Email"},"organization":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Organization"},"category":{"type":"string","pattern":"^(general|artist|label|platform|press|partner|legal|security)$","title":"Category","default":"general"},"message":{"type":"string","maxLength":5000,"minLength":10,"title":"Message"}},"additionalProperties":false,"type":"object","required":["name","email","message"],"title":"ContactFormBody"},"CreateSessionBody":{"properties":{"declaration_id":{"type":"string","title":"Declaration Id"},"biometric_consent":{"type":"boolean","title":"Biometric Consent","default":false}},"additionalProperties":false,"type":"object","required":["declaration_id"],"title":"CreateSessionBody"},"CreationModality":{"type":"string","enum":["daw_electronic","band_live","vocal_led","solo_instrumental","hybrid"],"title":"CreationModality"},"DAW":{"type":"string","enum":["ableton_live","logic_pro","fl_studio","pro_tools","reaper","bitwig","cubase","studio_one","garageband","other"],"title":"DAW"},"DeclarationCreate":{"properties":{"modality":{"$ref":"#/components/schemas/CreationModality"},"genre":{"type":"string","maxLength":64,"minLength":1,"title":"Genre"},"subgenre":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Subgenre"},"bpm":{"type":"number","exclusiveMaximum":400.0,"exclusiveMinimum":20.0,"title":"Bpm"},"key":{"type":"string","maxLength":16,"minLength":1,"title":"Key"},"roles":{"items":{"$ref":"#/components/schemas/ArtistRole"},"type":"array","minItems":1,"title":"Roles"},"instruments":{"items":{"$ref":"#/components/schemas/InstrumentClaim"},"type":"array","title":"Instruments"},"daw":{"anyOf":[{"$ref":"#/components/schemas/DAW"},{"type":"null"}]},"daw_other_name":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Daw Other Name"},"key_plugins":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Key Plugins"},"sample_packs":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Sample Packs"},"mic_setup":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Mic Setup"},"collaborators":{"items":{"$ref":"#/components/schemas/CollaboratorCredit"},"type":"array","title":"Collaborators"},"ai_tools_used":{"items":{"$ref":"#/components/schemas/AIToolUsage"},"type":"array","title":"Ai Tools Used"},"recording_context":{"$ref":"#/components/schemas/RecordingContext"},"creation_location":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Creation Location"},"sections":{"items":{"$ref":"#/components/schemas/SongSectionMarker"},"type":"array","maxItems":20,"minItems":1,"title":"Sections"},"time_invested":{"$ref":"#/components/schemas/TimeInvested"},"final_attestation_signed":{"type":"boolean","title":"Final Attestation Signed"},"attestation_signature_name":{"type":"string","maxLength":128,"minLength":1,"title":"Attestation Signature Name"}},"additionalProperties":false,"type":"object","required":["modality","genre","bpm","key","roles","recording_context","sections","time_invested","final_attestation_signed","attestation_signature_name"],"title":"DeclarationCreate","description":"The full 12-section declaration the artist submits."},"DeleteAccountBody":{"properties":{"confirm_email":{"type":"string","format":"email","title":"Confirm Email"},"confirm_delete":{"type":"boolean","title":"Confirm Delete"}},"additionalProperties":false,"type":"object","required":["confirm_email","confirm_delete"],"title":"DeleteAccountBody","description":"Confirmation payload for account deletion. The user must echo their\nemail and tick `confirm_delete` so a stolen cookie can't trigger this\nsilently — both fields are bound to the calling artist server-side."},"DeleteVerificationBody":{"properties":{"verification_name":{"type":"string","maxLength":200,"minLength":1,"title":"Verification Name"}},"additionalProperties":false,"type":"object","required":["verification_name"],"title":"DeleteVerificationBody"},"DiscrepancyResolution":{"properties":{"code":{"type":"string","title":"Code"},"chosen_resolution":{"type":"string","title":"Chosen Resolution"}},"additionalProperties":false,"type":"object","required":["code","chosen_resolution"],"title":"DiscrepancyResolution"},"ExternalLinkInput":{"properties":{"type":{"type":"string","maxLength":32,"minLength":1,"title":"Type"},"url":{"type":"string","maxLength":256,"minLength":8,"title":"Url"}},"additionalProperties":false,"type":"object","required":["type","url"],"title":"ExternalLinkInput"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HandleUpdateBody":{"properties":{"handle":{"type":"string","maxLength":30,"minLength":3,"title":"Handle"}},"additionalProperties":false,"type":"object","required":["handle"],"title":"HandleUpdateBody"},"InstrumentClaim":{"properties":{"instrument":{"type":"string","maxLength":64,"minLength":1,"title":"Instrument"},"performed_live":{"type":"boolean","title":"Performed Live"}},"additionalProperties":false,"type":"object","required":["instrument","performed_live"],"title":"InstrumentClaim"},"LoginBody":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"}},"additionalProperties":false,"type":"object","required":["email","password"],"title":"LoginBody"},"MLOptOutBody":{"properties":{"opt_out":{"type":"boolean","title":"Opt Out"}},"additionalProperties":false,"type":"object","required":["opt_out"],"title":"MLOptOutBody"},"OAuthAppRegisterBody":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name"},"redirect_uris":{"items":{"type":"string"},"type":"array","maxItems":10,"minItems":1,"title":"Redirect Uris"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Description"},"homepage_url":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Homepage Url"}},"additionalProperties":false,"type":"object","required":["name","redirect_uris"],"title":"OAuthAppRegisterBody"},"OIDCBridgeBody":{"properties":{"id_token":{"type":"string","maxLength":8192,"minLength":20,"title":"Id Token"}},"additionalProperties":false,"type":"object","required":["id_token"],"title":"OIDCBridgeBody"},"OIDCIssuerRegisterBody":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name"},"issuer":{"type":"string","maxLength":300,"minLength":8,"title":"Issuer"},"jwks_uri":{"type":"string","maxLength":300,"minLength":8,"title":"Jwks Uri"},"audience":{"type":"string","maxLength":200,"minLength":1,"title":"Audience"}},"additionalProperties":false,"type":"object","required":["name","issuer","jwks_uri","audience"],"title":"OIDCIssuerRegisterBody"},"OrgCreateBody":{"properties":{"name":{"type":"string","maxLength":120,"minLength":2,"title":"Name"},"contact_email":{"type":"string","format":"email","title":"Contact Email"}},"additionalProperties":false,"type":"object","required":["name","contact_email"],"title":"OrgCreateBody"},"OrgMemberAddBody":{"properties":{"artist_id":{"type":"string","minLength":4,"title":"Artist Id"},"role":{"type":"string","enum":["owner","admin","member"],"title":"Role","default":"member"}},"additionalProperties":false,"type":"object","required":["artist_id"],"title":"OrgMemberAddBody"},"PartnerSessionCreateBody":{"properties":{"track_id":{"type":"string","minLength":4,"title":"Track Id"},"declaration_id":{"type":"string","minLength":4,"title":"Declaration Id"},"consent_token":{"type":"string","minLength":20,"title":"Consent Token"}},"additionalProperties":false,"type":"object","required":["track_id","declaration_id","consent_token"],"title":"PartnerSessionCreateBody"},"PasswordResetConfirmBody":{"properties":{"token":{"type":"string","maxLength":256,"minLength":16,"title":"Token"},"new_password":{"type":"string","maxLength":128,"minLength":12,"title":"New Password"}},"additionalProperties":false,"type":"object","required":["token","new_password"],"title":"PasswordResetConfirmBody"},"PasswordResetRequestBody":{"properties":{"email":{"type":"string","format":"email","title":"Email"}},"additionalProperties":false,"type":"object","required":["email"],"title":"PasswordResetRequestBody"},"ProfileUpdateBody":{"properties":{"bio":{"anyOf":[{"type":"string","maxLength":280},{"type":"null"}],"title":"Bio"},"location":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Location"},"external_links":{"anyOf":[{"items":{"$ref":"#/components/schemas/ExternalLinkInput"},"type":"array","maxItems":5},{"type":"null"}],"title":"External Links"}},"additionalProperties":false,"type":"object","title":"ProfileUpdateBody","description":"All fields optional. Pass only the ones you want to change.\nSend empty string to clear an optional text field.\n\ndisplay_name and handle are intentionally absent: they're permanent\nidentifiers set at signup. Changes require support intervention, so they\naren't exposed on this surface."},"RecordingContext":{"type":"string","enum":["home_studio","professional_studio","live_performance","mixed_contexts"],"title":"RecordingContext"},"RefreshTokenBody":{"properties":{"refresh_token":{"type":"string","title":"Refresh Token"}},"additionalProperties":false,"type":"object","required":["refresh_token"],"title":"RefreshTokenBody"},"RenditionBody":{"properties":{"cert_id":{"type":"string","maxLength":128,"minLength":3,"title":"Cert Id"},"sha256":{"type":"string","title":"Sha256"},"fingerprint":{"type":"string","maxLength":400000,"minLength":4,"title":"Fingerprint","description":"base64 audio_fp computed from THAT artifact's own bytes"},"fingerprint2":{"anyOf":[{"type":"string","maxLength":4000000},{"type":"null"}],"title":"Fingerprint2"},"duration_ms":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Duration Ms"},"algorithm":{"type":"string","title":"Algorithm","default":"chromaprint"}},"additionalProperties":false,"type":"object","required":["cert_id","sha256","fingerprint"],"title":"RenditionBody","description":"Register an off-platform artifact's SHA → cert resolution (§12.2)."},"SandboxSeedCertBody":{"properties":{"cert_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Cert Id"},"verdict":{"type":"string","enum":["verified","partial","revoked"],"title":"Verdict","default":"verified"},"isrc":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Isrc"},"upc":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Upc"},"iswc":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Iswc"},"sha256":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Sha256"},"artist_handle":{"anyOf":[{"type":"string","maxLength":30},{"type":"null"}],"title":"Artist Handle"},"artist_display_name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Artist Display Name"},"track_title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Track Title"}},"additionalProperties":false,"type":"object","title":"SandboxSeedCertBody","description":"What a partner posts to seed a custom sandbox cert.\n\nEvery field is optional except the verdict. We fill the rest with safe\ndefaults so a one-liner POST is enough to get a working fixture."},"SignupBody":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","maxLength":128,"minLength":12,"title":"Password"},"display_name":{"type":"string","maxLength":60,"minLength":1,"title":"Display Name"},"handle":{"anyOf":[{"type":"string","maxLength":30},{"type":"null"}],"title":"Handle"},"accept_terms":{"type":"boolean","title":"Accept Terms","default":false},"confirm_age_18":{"type":"boolean","title":"Confirm Age 18","default":false}},"additionalProperties":false,"type":"object","required":["email","password","display_name"],"title":"SignupBody"},"SongSectionMarker":{"properties":{"label":{"type":"string","maxLength":32,"minLength":1,"title":"Label"},"start_s":{"type":"number","minimum":0.0,"title":"Start S"},"end_s":{"type":"number","exclusiveMinimum":0.0,"title":"End S"}},"additionalProperties":false,"type":"object","required":["label","start_s","end_s"],"title":"SongSectionMarker"},"StudioCheckBody":{"properties":{"camera_ok":{"type":"boolean","title":"Camera Ok"},"microphone_ok":{"type":"boolean","title":"Microphone Ok"},"room_acoustics_ok":{"type":"boolean","title":"Room Acoustics Ok"},"face_match_ok":{"type":"boolean","title":"Face Match Ok"},"midi_connected":{"type":"boolean","title":"Midi Connected","default":false},"screen_share_active":{"type":"boolean","title":"Screen Share Active","default":false}},"additionalProperties":false,"type":"object","required":["camera_ok","microphone_ok","room_acoustics_ok","face_match_ok"],"title":"StudioCheckBody"},"SubmitChallengeBody":{"properties":{"final_chain_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Final Chain Hash"},"captured_at_client":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Captured At Client"},"chunk_count":{"type":"integer","title":"Chunk Count","default":0},"structured_answer":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Structured Answer"}},"additionalProperties":false,"type":"object","title":"SubmitChallengeBody"},"TimeInvested":{"type":"string","enum":["single_session","few_days","few_weeks","months_or_more"],"title":"TimeInvested"},"TrackIdentifiersBody":{"properties":{"isrc":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Isrc"},"upc":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Upc"},"iswc":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Iswc"}},"additionalProperties":false,"type":"object","title":"TrackIdentifiersBody","description":"Optional partial-update of catalogue identifiers on an uploaded track.\n\nAll three fields are optional. Pass an empty string to clear an existing\nvalue. Anything missing from the payload is left untouched (so a partner\nthat only just got an ISRC can POST `{\"isrc\": \"...\"}` without also having\nto re-send the UPC and ISWC they set earlier)."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VerifyAudioBody":{"properties":{"fingerprint":{"type":"string","maxLength":400000,"minLength":4,"title":"Fingerprint","description":"base64 audio_fp computed from THIS artifact's own bytes"},"fingerprint2":{"anyOf":[{"type":"string","maxLength":4000000},{"type":"null"}],"title":"Fingerprint2","description":"base64 audio_fp2 (required only if the cert is high-risk)"},"duration_ms":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Duration Ms","description":"artifact duration in ms — REQUIRED for a positive match (the duration-within-tolerance gate, §9.1); omit and the result is never matched"},"algorithm":{"type":"string","title":"Algorithm","default":"chromaprint"}},"additionalProperties":false,"type":"object","required":["fingerprint"],"title":"VerifyAudioBody","description":"One artifact's perceptual fingerprint, to verify against one cert."},"WebhookCreateBody":{"properties":{"url":{"type":"string","maxLength":2048,"minLength":8,"title":"Url"},"events":{"items":{"type":"string"},"type":"array","maxItems":3,"minItems":1,"title":"Events"},"description":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Description"},"scope":{"type":"string","enum":["artist","global"],"title":"Scope","default":"artist"}},"additionalProperties":false,"type":"object","required":["url","events"],"title":"WebhookCreateBody"},"_AcceptPolicyBody":{"properties":{"fp_match_threshold":{"type":"number","maximum":1.0,"exclusiveMinimum":0.0,"title":"Fp Match Threshold"},"fp2_match_threshold":{"anyOf":[{"type":"number","maximum":1.0,"exclusiveMinimum":0.0},{"type":"null"}],"title":"Fp2 Match Threshold"},"calibration_report":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Calibration Report"}},"additionalProperties":false,"type":"object","required":["fp_match_threshold"],"title":"_AcceptPolicyBody","description":"Calibration output (§9.3): the threshold a calibration run produced."},"_AdminCreateCouponBody":{"properties":{"code":{"type":"string","maxLength":64,"minLength":2,"title":"Code"},"campaign":{"type":"string","maxLength":128,"minLength":1,"title":"Campaign"},"credits":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Credits"},"max_redemptions":{"anyOf":[{"type":"integer","maximum":1000000.0,"minimum":1.0},{"type":"null"}],"title":"Max Redemptions"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"additionalProperties":false,"type":"object","required":["code","campaign","credits"],"title":"_AdminCreateCouponBody"},"_AdminGrantCreditsBody":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"credits":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Credits"},"note":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Note"}},"additionalProperties":false,"type":"object","required":["email","credits"],"title":"_AdminGrantCreditsBody"},"_AdminLoginBody":{"properties":{"username":{"type":"string","maxLength":128,"minLength":1,"title":"Username"},"password":{"type":"string","maxLength":256,"minLength":1,"title":"Password"}},"additionalProperties":false,"type":"object","required":["username","password"],"title":"_AdminLoginBody"},"_AdminRevokeCertBody":{"properties":{"reason":{"type":"string","maxLength":500,"minLength":3,"title":"Reason"}},"additionalProperties":false,"type":"object","required":["reason"],"title":"_AdminRevokeCertBody"},"_AdminRotateKeyBody":{"properties":{"new_kid":{"type":"string","maxLength":64,"minLength":4,"title":"New Kid"},"new_public_key_pem":{"type":"string","minLength":40,"title":"New Public Key Pem"}},"additionalProperties":false,"type":"object","required":["new_kid","new_public_key_pem"],"title":"_AdminRotateKeyBody"},"_BillingCheckoutBody":{"properties":{"package_id":{"type":"string","maxLength":64,"minLength":1,"title":"Package Id"},"country":{"anyOf":[{"type":"string","maxLength":2},{"type":"null"}],"title":"Country"}},"additionalProperties":false,"type":"object","required":["package_id"],"title":"_BillingCheckoutBody"},"_CouponRedeemBody":{"properties":{"code":{"type":"string","maxLength":64,"minLength":2,"title":"Code"}},"additionalProperties":false,"type":"object","required":["code"],"title":"_CouponRedeemBody"}}},"tags":[{"name":"certificates","description":"Verification certificates. Public reads (`GET`) — paid bulk + list reads require an API key. Manifests are Ed25519-signed; verify offline against `/.well-known/trackorigin-public-keys.json`."},{"name":"artists","description":"Public artist profile + their certificate catalogue."},{"name":"exports","description":"Authenticated CSV/JSON catalogue exports for compliance + royalty workflows."},{"name":"transparency","description":"Append-only Certificate Transparency log + signed tree head. A DSP compliance team can pin one STH and detect tampering against any later state."},{"name":"partner","description":"Partner integration surface: verify-on-behalf with an artist-signed consent token. Requires a secret key (`to_sk_…`)."},{"name":"webhooks","description":"Outbound webhook contract: event types, signing algorithm, retry policy. Subscribe via `POST /v1/me/webhooks` from the artist UI."},{"name":"sandbox","description":"Test-mode utilities. Keys prefixed `to_pk_test_` / `to_sk_test_` are unmetered, never fire real webhooks, and read from a separate sandbox cert store."},{"name":"account","description":"Whoami + balance + usage for the calling key."}]}