{"openapi":"3.1.0","info":{"title":"gitbuyer","version":"0.1.0"},"paths":{"/assets/{ver}/app.css":{"get":{"summary":"App Css","description":"The one stylesheet, addressed by its content hash (pages.CSS_VERSION).\n\nA matching version is immutable — the URL itself changes when the CSS\ndoes. A MISMATCHED version means stale HTML (cached by a proxy or an\nopen tab across a deploy) is asking for CSS we no longer have; serving\nthe current sheet un-cached keeps that page styled instead of naked.","operationId":"app_css_assets__ver__app_css_get","parameters":[{"name":"ver","in":"path","required":true,"schema":{"type":"string","title":"Ver"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Home","operationId":"home__get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/healthz":{"get":{"summary":"Healthz","operationId":"healthz_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/llms.txt":{"get":{"summary":"Llms","operationId":"llms_llms_txt_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/{slug}":{"get":{"summary":"Api Docs","description":"The human docs — a grouped sidebar, one page per topic, every block\ncopy-paste-runnable. /llms.txt stays the agent version; /docs the\nOpenAPI.","operationId":"api_docs_api__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api":{"get":{"summary":"Api Docs","description":"The human docs — a grouped sidebar, one page per topic, every block\ncopy-paste-runnable. /llms.txt stays the agent version; /docs the\nOpenAPI.","operationId":"api_docs_api_get","parameters":[{"name":"slug","in":"query","required":false,"schema":{"type":"string","default":"overview","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/magic-link":{"post":{"summary":"Magic Link","operationId":"magic_link_auth_magic_link_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MagicLinkRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Magic Link Auth Magic Link Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/verify":{"post":{"summary":"Verify","operationId":"verify_auth_verify_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Verify Auth Verify Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"Verify From Link","description":"The GET a human's browser hits from the email. Sets the cookie and\nlands them where they belong: a BRAND-NEW account on /welcome (the\nguided first-sale path), an existing one on their stores — or back\nwhere sign-in interrupted them (the OAuth consent screen), same-origin\npaths only and always winning.","operationId":"verify_from_link_auth_verify_get","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","title":"Token"}},{"name":"return_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/logout":{"post":{"summary":"Logout","description":"One logout, two callers: the sidebar's Sign-out form (a browser,\nwants to LAND somewhere signed-out) and the CLI/API (wants the 204\nit always got). Sessions are stateless signed tokens, so clearing\nthe cookie is the whole of a browser logout; a bearer session ends\nby discarding the token.","operationId":"logout_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/whoami":{"get":{"summary":"Whoami","operationId":"whoami_auth_whoami_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Whoami Auth Whoami Get"}}}}}}},"/my/account":{"patch":{"summary":"Update Account","description":"Where the money goes, once per account: these prefill and backstop\nevery onboard, so listing a repo is one pasted URL. Per-store wallets\n(set at listing or on the manage page) always win over these. Sellers\nnever need a checkout402 account — the wallet named here is where\nbuyers' USDC settles, on-chain, directly.","operationId":"update_account_my_account_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountPatch"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Update Account My Account Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/my/wallets/refresh":{"post":{"summary":"Refresh Wallet Balances","description":"Read the configured payout wallets' USDC balances from the chains,\nstore them, return them. PULL-ONLY: this is the only place gitbuyer\never contacts an RPC, so page loads never depend on one — and the\ncooldown keeps even an enthusiastic thumb polite to public endpoints.","operationId":"refresh_wallet_balances_my_wallets_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Refresh Wallet Balances My Wallets Refresh Post"}}}}}}},"/my/keys":{"get":{"summary":"List Keys","operationId":"list_keys_my_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Keys My Keys Get"}}}}}},"post":{"summary":"Mint Key","description":"A fresh gb_ key for this account — the headless door, so an agent\ncan sell and manage without the magic-link inbox loop. The raw key\nappears in THIS response and nowhere else, ever: only its hash is\nstored, and GET /my/keys returns hints.","operationId":"mint_key_my_keys_post","responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Mint Key My Keys Post"}}}}}}},"/my/keys/{key_id}":{"delete":{"summary":"Revoke Key","description":"Revocation is immediate — the next request on that key 401s. A key\nid from another account is the same 404 as a missing one.","operationId":"revoke_key_my_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/internal/c402-events":{"post":{"summary":"C402 Events","description":"checkout402's webhook receiver — how a sale becomes OUR row the\nsecond it settles. Verified by Ed25519 against checkout402's PUBLIC\nkey (fetched, cached, no shared secret) — so the receiver is armed by\ndefault, needing only that the URL be registered. A receiver still\nholding the old symmetric secret keeps working via the HMAC fallback.\n\nIdempotent by payment_id, so redeliveries are welcome; a checkout we\ncannot resolve answers 200 (a delisted store's late sale is not an\nerror the sender can fix by retrying).","operationId":"c402_events_internal_c402_events_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/github/connect":{"get":{"summary":"Github Connect","description":"The button: bounce the signed-in seller to the App's install page on\nGitHub, carrying a signed state naming their account. `return_to`\n(validated to a same-origin path HERE, and again on the way back) rides\nthe state so the page that sent the seller gets them back — this is\nwhat closes the List-a-repo page's round trip.","operationId":"github_connect_github_connect_get","parameters":[{"name":"return_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/github/setup":{"get":{"summary":"Github Setup","description":"GitHub's post-install redirect — where the installation becomes OURS.\n\nThe seller is named by the signed state (first choice: it survived the\ncross-site hop) or the live session cookie (the seller who clicked\nConfigure from GitHub's side later). The installation is then verified\nWITH GITHUB — account and a freshly minted token — before anything is\nstored: a forged installation_id dies here, not at delivery. The token\nlands at checkout402 as this installation's rotating origin key; the\nrefresher keeps it fresh from then on.","operationId":"github_setup_github_setup_get","parameters":[{"name":"installation_id","in":"query","required":true,"schema":{"type":"integer","title":"Installation Id"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}},{"name":"setup_action","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Setup Action"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/github/signin":{"get":{"summary":"Github Signin","description":"Sign in with GitHub — the identity door. With ?owner= it is a\nCLAIM: the callback insists the verified GitHub login IS that owner\nbefore anything attaches. Signed state carries who was signed in (to\nLINK rather than switch), the claim target, and where to land.","operationId":"github_signin_github_signin_get","parameters":[{"name":"owner","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner"}},{"name":"return_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/github/oauth/callback":{"get":{"summary":"Github Oauth Callback","description":"GitHub's answer to /github/signin. The state is ours and signed;\nthe identity is GitHub's and fetched live. Only after both check out\ndoes any seller row change.","operationId":"github_oauth_callback_github_oauth_callback_get","parameters":[{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/github/oauth/email":{"post":{"summary":"Github Oauth Email","description":"The fallback when GitHub shares no address: the identity is\nalready verified (it rode here signed), the human supplies where\nreceipts and sign-in links go.","operationId":"github_oauth_email_github_oauth_email_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthEmailRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Github Oauth Email Github Oauth Email Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/seeded/{owner}/{repo}":{"delete":{"summary":"Seeded Remove","description":"Take one indexed repo off the claimed page — delete plus\ntombstone, so no future import resurrects it. A non-owner's attempt\nis the same 404 as a missing row (the delete_listing contract).","operationId":"seeded_remove_seeded__owner___repo__delete","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","title":"Repo"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Seeded Remove Seeded  Owner   Repo  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/seeded/{owner}/remove-all":{"post":{"summary":"Seeded Remove All","description":"The full exit: every indexed repo goes AND the owner opts out of\nall future indexing. Gated on the proven identity, not on stamped\nrows — the whole point is that this works even if stamping missed.\nTwo identities qualify, same two as everywhere: OAuth proved this\nseller IS @owner, or this seller's App installation lives on @owner\n(the org proof — an org admin installed it).","operationId":"seeded_remove_all_seeded__owner__remove_all_post","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Seeded Remove All Seeded  Owner  Remove All Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/my/github":{"get":{"summary":"My Github","description":"The GitHub connection for machines — same truth as the dashboard\ncard: installations, their repos, and any orphaned listings.","operationId":"my_github_my_github_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response My Github My Github Get"}}}}}}},"/github/installations/{installation_id}":{"delete":{"summary":"Github Disconnect","description":"Sever the connection from gitbuyer's side: listings unbind (falling\nback to PAT-or-public delivery), the refresher stops renewing the\ntoken, and within the hour the credential is dead at checkout402 too.\nFull revocation lives on GitHub (uninstall the app) — say so.","operationId":"github_disconnect_github_installations__installation_id__delete","parameters":[{"name":"installation_id","in":"path","required":true,"schema":{"type":"integer","title":"Installation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Github Disconnect Github Installations  Installation Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/signin":{"get":{"summary":"Signin","description":"The browser door to an account. Already signed in? Straight through —\nhonouring return_to either way, so an OAuth authorize that bounced here\nresumes instead of dead-ending on the dashboard.","operationId":"signin_signin_get","parameters":[{"name":"return_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/terms":{"get":{"summary":"Terms Page","operationId":"terms_page_terms_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/privacy":{"get":{"summary":"Privacy Page","operationId":"privacy_page_privacy_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/dmca":{"get":{"summary":"Dmca Page","operationId":"dmca_page_dmca_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/guide/buying":{"get":{"summary":"Buying Guide","description":"The buyer's guide — public, both faces. The JSON face is the\nmachine-readable version the deal cards and llms.txt point agents at,\nso an agent can read the steps and tell its human exactly what to do.","operationId":"buying_guide_guide_buying_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/guide/qr/{chain}/{address}.png":{"get":{"summary":"Funding Qr","description":"The minted address as a QR — what an exchange's withdrawal screen\nscans instead of a human re-typing 0x… by hand. Encodes the PLAIN\naddress, deliberately: address-only QRs are the one format every\nexchange app reads, and the network choice stays a human decision the\nguide walks through. Click-driven from the guide page, so the address\nreaches us only when the visitor asks for the image.\n\nUnauthenticated BY DESIGN — buyers have no accounts, and the wallet\nthis QR serves was minted client-side seconds ago. What is guarded is\nthe resource, not the identity: per-IP shaping, and a refusal to be\nhotlinked into other sites' pages wearing our domain.","operationId":"funding_qr_guide_qr__chain___address__png_get","parameters":[{"name":"chain","in":"path","required":true,"schema":{"type":"string","title":"Chain"}},{"name":"address","in":"path","required":true,"schema":{"type":"string","title":"Address"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/guide/funded":{"get":{"summary":"Funding Check","description":"Did the withdrawal arrive? One click, one chain lookup, the same\nhelpers the dashboard's payout-wallet balances use. Button-driven on\npurpose: no polling loop, so the public RPCs behind chains.py see one\nrequest per human click and nothing else. `usdc: null` means the\nchain did not answer — different from \"0\", which means it answered\nand the money is not there yet.\n\nThe limiter here protects a SHARED resource: the public RPCs answer\nfor this endpoint and for the dashboard's payout balances from the\nsame server IP, so an anonymous hammer on this route could get the\nwhole app rate-limited upstream. Refused checks never reach the\nchain.","operationId":"funding_check_guide_funded_get","parameters":[{"name":"chain","in":"query","required":true,"schema":{"type":"string","title":"Chain"}},{"name":"address","in":"query","required":true,"schema":{"type":"string","title":"Address"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/welcome":{"get":{"summary":"Welcome","description":"The first-sale wizard: wallets → GitHub → first listing, every step\nskippable. A brand-new sign-in lands here (the `created` flag in\n/auth/verify); everyone else reaches it only by choosing to — and a\nseller with nothing left to set up bounces straight to their stores.","operationId":"welcome_welcome_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/my/new":{"get":{"summary":"List A Repo","description":"The List-a-repo page: permanent, signed-in, and it NEVER redirects\naway however complete the account is — the wizard's step 3 is for\nfirst-timers; this page is for every repo after. It owns no listing\nlogic: it renders the same pick-list rows the dashboard uses and posts\nto POST /repos, where the live-mode gates stay.\n\nThe page's shape follows the install mode GitHub reports\n(repository_selection: \"all\" or \"selected\"); when GitHub does not say,\n\"selected\" is the safe reading. Self-heal first, same as the dashboard:\na lost redirect must never cost a seller their repos — and the return\ntrip from /github/connect lands here with the fresh selection already\nfetched (github_setup busts the repos cache for that installation).","operationId":"list_a_repo_my_new_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/my/stores":{"get":{"summary":"My Stores","description":"A seller's own listings — HTML dashboard for a human, JSON for the\nCLI. Behind sign-in either way. `?listed=owner/repo` (set by the List\nflows) surfaces the just-created store's links in a success card —\nhonored only when the row is genuinely this seller's.","operationId":"my_stores_my_stores_get","parameters":[{"name":"listed","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Listed"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/my/stores/{owner}/{repo}":{"get":{"summary":"Manage Store","description":"One store's controls: price, wallets, key, takedown. Owner-only —\nand a 404 for everyone else, which declines to confirm the row exists.","operationId":"manage_store_my_stores__owner___repo__get","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","title":"Repo"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/my/buying":{"get":{"summary":"Buyer Panel","description":"The buyer's home — optional, post-hoc, and custody-free by\nconstruction: addresses and receipts, never keys. Checkout stays\naccount-less; this page is where a HUMAN chooses to keep the pieces\n(watched wallets, auto-found purchases) an agent would keep in its\ntranscript.","operationId":"buyer_panel_my_buying_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/my/wallets/watch":{"post":{"summary":"Watch Wallet","operationId":"watch_wallet_my_wallets_watch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WatchWallet"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Watch Wallet My Wallets Watch Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/my/wallets/unwatch":{"post":{"summary":"Unwatch Wallet","operationId":"unwatch_wallet_my_wallets_unwatch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WatchWallet"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Unwatch Wallet My Wallets Unwatch Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/my/sales":{"get":{"summary":"My Sales","description":"The account-wide sales ledger — HTML for the seller, JSON for the\nCLI and agents. Reads the mirrored sales table (webhook-fed,\nreconciler-backfilled), the same rows the dashboard's money numbers\ncome from, so the ledger and the analytics can never disagree.","operationId":"my_sales_my_sales_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/my/sales.csv":{"get":{"summary":"My Sales Csv","description":"The ledger as a file for a spreadsheet or an accountant. Same rows\nas /my/sales, every column, no pagination: a sales history that needs\npagination has earned a database, and until then one file is what a\nbookkeeper actually wants.","operationId":"my_sales_csv_my_sales_csv_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/my/stores/{owner}/{repo}/sales":{"get":{"summary":"Store Sales","description":"One store's sales as JSON — what `gitbuyer sales` reads. Owner-gated\nexactly like the manage page (a stranger's request is the same 404 as\na missing row). Reads the ARCHIVE, not the live ledger scoped to the\ncurrent checkout: a reprice or a delist-and-relist rotates the\ncheckout URL, and the ledger-scoped read swore a repo with real\nrevenue had none while analytics showed the money. Same rows as\nanalytics, by construction — and no 502 when checkout402 blinks.","operationId":"store_sales_my_stores__owner___repo__sales_get","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","title":"Repo"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Store Sales My Stores  Owner   Repo  Sales Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/my/stores/{owner}/{repo}/traffic":{"get":{"summary":"Store Traffic","description":"One store's traffic as JSON — the numbers behind the manage page's\npanel, for the CLI and anything else that wants them. Owner-gated\nexactly like sales: a stranger's request is the same 404 as a missing\nrow. Unlike sales this never 502s — the ledger is our own database, and\nan empty week really is an empty week (the geo block says whether\nlocation reporting could have known better).","operationId":"store_traffic_my_stores__owner___repo__traffic_get","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","title":"Repo"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Store Traffic My Stores  Owner   Repo  Traffic Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/repos/{owner}/{repo}":{"delete":{"summary":"Delist","description":"Takedown. Owner-gated at the query; also voids the checkout at\ncheckout402 so a delisted repo stops being payable, not just unlisted.","operationId":"delist_repos__owner___repo__delete","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","title":"Repo"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delist Repos  Owner   Repo  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"summary":"Update Listing","description":"Edit a store without re-listing it: price, wallets, key, preview.\n\nThe mechanics mirror onboard — fresh GitHub view, fresh checkout — with\ntwo lifecycle guarantees onboard cannot give:\n\n* the saved PAT survives unless the update speaks about it, and\n* the REPLACED checkout is voided at checkout402, because agents cache\n  checkout URLs and a stale one would keep selling at the old price to\n  the old wallets.","operationId":"update_listing_repos__owner___repo__patch","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","title":"Repo"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateListing"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Listing Repos  Owner   Repo  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/repos":{"post":{"summary":"Onboard","operationId":"onboard_repos_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Onboard"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Onboard Repos Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/avatar/{owner}":{"get":{"summary":"Owner Avatar","description":"The owner's GitHub avatar, served from OUR origin (gh.avatar proxies\nand caches it) — no gitbuyer page ever loads a third-party image, so no\nthird party ever logs a store visitor. Its own path, not\n/r/{owner}/avatar, which the store route would swallow as a repo name.","operationId":"owner_avatar_avatar__owner__get","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/og/{owner}.png":{"get":{"summary":"Storefront Og","description":"The storefront's personalized share card — the image its link\nunfurls with. Public facts only (avatar, name, tagline, count), and\na 404 for an owner with nothing listed: the card must not enumerate\nwhat the storefront itself would not.","operationId":"storefront_og_og__owner__png_get","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/og/{owner}/{repo}.png":{"get":{"summary":"Repo Og","description":"The repo page's share card — the deal as an image, drawn from the\nlisting row's snapshot (no GitHub fetch on a scraper's clock). Same\n404 rule as the page: an unlisted repo has no card.","operationId":"repo_og_og__owner___repo__png_get","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","title":"Repo"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/receipt/{payment_id}":{"get":{"summary":"Buyer Receipt","description":"The buyer's copy of a purchase — the storefront face of a receipt.\n\ncheckout402's /r/{payment_id} stays the money record; this page is the\nPURCHASE record (what repo, from whose storefront, where to act), fed\nby the webhook-mirrored sales table. Public by unguessable id, exactly\nlike the upstream receipt: the id is the capability, and this page\ngrants nothing but the view. The payer address deliberately does not\nappear here: this is the face a buyer shares, and sharing a purchase\nmust not mean sharing a wallet trail.","operationId":"buyer_receipt_receipt__payment_id__get","parameters":[{"name":"payment_id","in":"path","required":true,"schema":{"type":"string","title":"Payment Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/r/{owner}/{repo}/request-supported":{"post":{"summary":"Request Supported","description":"The raised hand: a visitor or agent wants a maintained,\nagent-ready version of a free repo. Anonymous, no account — the\nCOUNT is the signal, and it becomes the owner's claim nudge.","operationId":"request_supported_r__owner___repo__request_supported_post","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","title":"Repo"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/agents":{"get":{"summary":"Agents Page","description":"The connection moment: one paste per client, plus the habit\nsnippet. The header's one filled button lands here. (/privacy\nalready exists, registered with the other legal pages.)","operationId":"agents_page_agents_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/claude":{"get":{"summary":"Claude Setup Page","description":"The recommendation made a page: use gitbuyer through Claude.\nLinked from the footer; llms.txt points agents at the same door.","operationId":"claude_setup_page_claude_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/robots.txt":{"get":{"summary":"Robots","operationId":"robots_robots_txt_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/sitemap.xml":{"get":{"summary":"Sitemap","description":"The catalog's shape, told to crawlers: every storefront and every\nrepo page, both halves of the marketplace. One flat file while the\nmarketplace fits in one; a sitemap index over numbered shards once\nthe catalog outgrows the protocol's 50,000-URL cap.","operationId":"sitemap_sitemap_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap-{n}.xml":{"get":{"summary":"Sitemap Shard","operationId":"sitemap_shard_sitemap__n__xml_get","parameters":[{"name":"n","in":"path","required":true,"schema":{"type":"integer","title":"N"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/search":{"get":{"summary":"Search","description":"Discovery: the whole marketplace, filtered by what the visitor is\nbuilding. Listed and indexed repos in one answer, listed first; no\nquery is a browse, which makes this URL the /explore surface too.\nSame two faces, same filters — a browser's chips and an agent's\nparameters are the one vocabulary (min_stars, license or\n'permissive', updated_within_days, starters, sort).","operationId":"search_search_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Limit"}},{"name":"min_stars","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Min Stars"}},{"name":"license","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"License"}},{"name":"updated_within_days","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Updated Within Days"}},{"name":"starters","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Starters"}},{"name":"repo_kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Repo Kind"}},{"name":"sort","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sort"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/start":{"get":{"summary":"Start Answer","description":"The answer machine: what are you building, and here is the base.\nOne pick with the why and the paste command, two runners-up, and\nthe browse door for whoever wants the full grid — the same verdict\non both faces, so a human's terminal and an agent's tool call start\nfrom the same repo.","operationId":"start_answer_start_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/r/{owner}":{"get":{"summary":"Storefront Page","description":"One seller's public storefront: their whole inventory of listed\nrepos under one GitHub owner.\n\nThe URL is the parent of /r/{owner}/{repo} on purpose: a buyer who\ntrims a repo page URL by one segment should land on the seller, not a\n404. Keyed on the GitHub owner, not the seller account: the owner name\nis what buyers see in every listing URL, and it needs no join to\nrender. Same two faces as everything else — HTML for a human, JSON\nfor agents.","operationId":"storefront_page_r__owner__get","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/r/{owner}/{repo}":{"get":{"summary":"Repo Page","operationId":"repo_page_r__owner___repo__get","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","title":"Repo"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/r/{owner}/{repo}/f/{path}":{"get":{"summary":"File Page","description":"One file, GitHub-style — the moment a code buyer decides.\n\nContents are served only inside the preview policy; a locked file is not\nan error, it is the SALE: the page answers with the price and the buy\nlink, which is exactly what a buyer who went looking for that file needs.","operationId":"file_page_r__owner___repo__f__path__get","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string","title":"Owner"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","title":"Repo"}},{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/mcp":{"post":{"tags":["mcp"],"summary":"Mcp Endpoint","operationId":"mcp_endpoint_mcp_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-protected-resource/mcp":{"get":{"tags":["oauth"],"summary":"Protected Resource","operationId":"protected_resource__well_known_oauth_protected_resource_mcp_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"tags":["oauth"],"summary":"Protected Resource","operationId":"protected_resource__well_known_oauth_protected_resource_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-authorization-server/mcp":{"get":{"tags":["oauth"],"summary":"Authorization Server","operationId":"authorization_server__well_known_oauth_authorization_server_mcp_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"tags":["oauth"],"summary":"Authorization Server","operationId":"authorization_server__well_known_oauth_authorization_server_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/oauth/register":{"post":{"tags":["oauth"],"summary":"Register","operationId":"register_oauth_register_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/oauth/authorize":{"get":{"tags":["oauth"],"summary":"Authorize","operationId":"authorize_oauth_authorize_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/oauth/consent":{"post":{"tags":["oauth"],"summary":"Consent","operationId":"consent_oauth_consent_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/oauth/token":{"post":{"tags":["oauth"],"summary":"Token","operationId":"token_oauth_token_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AccountPatch":{"properties":{"wallet_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Base"},"wallet_solana":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Solana"},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url"},"storefront_tagline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Storefront Tagline"}},"type":"object","title":"AccountPatch","description":"Account-default payout wallets, the sale webhook and the storefront\ntagline. Absent keeps, \"\" clears — the same three-state contract the\nlisting PATCH speaks."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"MagicLinkRequest":{"properties":{"email":{"type":"string","title":"Email"},"return_to":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To"}},"type":"object","required":["email"],"title":"MagicLinkRequest"},"OAuthEmailRequest":{"properties":{"pending":{"type":"string","title":"Pending"},"email":{"type":"string","title":"Email"}},"type":"object","required":["pending","email"],"title":"OAuthEmailRequest"},"Onboard":{"properties":{"github_url":{"type":"string","title":"Github Url"},"github_pat":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Github Pat"},"wallet_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Base"},"wallet_solana":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Solana"},"price_usd":{"type":"string","title":"Price Usd","default":"20.00"},"preview_paths":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Preview Paths"}},"type":"object","required":["github_url"],"title":"Onboard","description":"The WHOLE product: a repo, a key, where the money goes. Done.\n\nNo gitbuyer account, no checkout402 account, no OAuth. The listing is\ncreated on gitbuyer's own platform key with `pay_to` set to YOUR wallet —\nsettlement is non-custodial and on-chain, so buyers' USDC lands in your\nwallet directly; gitbuyer cannot touch it even on a bad day."},"UpdateListing":{"properties":{"price_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Usd"},"wallet_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Base"},"wallet_solana":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet Solana"},"github_pat":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Github Pat"},"preview_paths":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Preview Paths"}},"type":"object","title":"UpdateListing","description":"Partial update: only the fields the client SENDS change.\n\n`github_pat` has three states, matching the storage contract: absent\nkeeps the saved key, a string replaces it, and \"\" removes it (for a\nrepo that has since gone public — a revoked key left on file would\nbreak the preview fetches). Wallets sent as \"\" drop that rail, but at\nleast one rail must survive the merge."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VerifyRequest":{"properties":{"token":{"type":"string","title":"Token"}},"type":"object","required":["token"],"title":"VerifyRequest"},"WatchWallet":{"properties":{"chain":{"type":"string","title":"Chain"},"address":{"type":"string","title":"Address"},"holds_keys":{"type":"boolean","title":"Holds Keys","default":false},"confirm_payout_reuse":{"type":"boolean","title":"Confirm Payout Reuse","default":false}},"type":"object","required":["chain","address"],"title":"WatchWallet"}}}}