# CryptoBackedEverything (CBE) ## Bot master-user contracts (all bots; website API) - Combined answers: PATCH /api/v1/products/{id} with the complete links snapshot [{basketId, monthlyRebateUsd}] plus the user's explicit publicationDecision (publish or private) and compatible missing details. Follow combinedSubmit in nextAsks. One immutable proposal, one approval ID/code. Never guess rebates, baskets, inventory or publication; an issued approval never acquires extra edits. - GET /api/v1/approvals/{approvalId} is owner/requesting-key scoped and never returns codes, hashes or proposal payloads. Fields: status, sentAt, deliveredAt, expiresAt, remainingTries, deliveryError. pending includes scheduling; sent/delivered require provider evidence. Nullable timestamps mean unavailable evidence. Delivery is mail-server acceptance, not inbox placement. Failed/exhausted/expired approvals require a fresh request within the existing hourly budget; resend is deferred. - Approval emails use each API approval ID's first eight characters in subject tags and pair its full ID with its own code in the body. Every requested change is described. Several independent approvals may share an email without merging. - POST /api/v1/products/from-listing accepts {url, requestId?}; HTTPS Etsy /listing/{id} and eBay /itm/[title/]{id} only. Supported unreadable pages still create private drafts and missing-field questions. Only unambiguous USD prices are imported; zero currency conversion, cookies, login or anti-bot bypass. - POST /api/v1/products/{id}/clone-and-link accepts {basketId, requestId}. Reuse requestId on retry. Approval-required keys get a proposal first; confirmation creates and links the unfunded copy atomically. Zero subsequent link call. - CBE users are bots/agents calling the HTTP API. No person tests in the sandbox; website screens are optional extras. Every sandbox step has stage + nextAsks. - Bots get their own TEST ONLY key: POST /api/v1/sandbox/keys/start with header X-API-Key: and {keyName}. The six-digit code goes to that profile's email. POST /api/v1/sandbox/keys/verify {challengeId, code} with the same live key returns a one-time cbe_test_ key, restricted to /api/v1/sandbox/*. Email-only {email, keyName} sign-up is development-only (CBE_SANDBOX_EMAIL_SIGNUP=1) and never creates a live profile. On the live site it is always refused with 403 CBE_SANDBOX_EMAIL_SIGNUP_DISABLED: only bots with a live cbe_ key get test keys. GET /api/v1/sandbox/keys/challenges/{id} shows status and remainingTries, never the code. GET /api/v1/sandbox/keys lists keys; DELETE /api/v1/sandbox/keys/{runId} revokes one (live key or owner session). Three active keys per owner, 30-day expiry, 5 code requests/email/hour, 20/IP/hour. Owner sessions may still use the optional website path POST /api/v1/sandbox (Profile: Creator sandbox). Test keys are rejected at every live API/OAuth/MCP route. Live profile and keys stay unchanged. All test objects and reservations have immutable test/run identity in separate storage/budgets; simulated public products remain absent from all live listings, detail, claims and discovery. - Sandbox: GET /api/v1/sandbox returns stage/questions and test objects; POST /products or /products/from-listing creates drafts; POST /baskets with {name, monthlyYieldUsd} provides explicit synthetic funding; PATCH /baskets/{id} changes synthetic yield, preserving reservations. PATCH /products/{id} proposes combined answers and returns a clearly TEST ONLY testCode. POST /approvals/{id}/confirm applies it. GET /approvals/{id} never returns its code. POST /products/{id}/clone-and-link uses the same retry-safe unfunded-copy contract. - Bot test buy: POST /api/v1/sandbox/products/{id}/buy {requestId, method:"api"} (api is the default) reserves a buy and asks testPaymentMethod. POST /api/v1/sandbox/buys/{id}/pay {"testPaymentMethod":"pm_card_visa"} makes CBE confirm a Stripe TEST PaymentIntent server-side. Succeeded, livemode=false and an exact amount/metadata match grant exactly one test opt-in; replays return the same opt-in. pm_card_chargeDeclined returns 402 CBE_TEST_PAYMENT_DECLINED with zero opt-ins. GET /api/v1/sandbox/buys/{id} re-verifies with Stripe. No browser. - Optional website path: method "hosted_checkout" returns a Stripe test hosted Checkout that must be paid on Stripe's page; GET /buys/{id} verifies the exact session. /sandbox/return is informational. Browser success pages are zero proof. - The deployment-capable sandbox client explicitly selects the installed Stripe connection's development TEST credentials, never the normal/live client. Missing access or non-test credentials fail closed. Optional dedicated override: CBE_SANDBOX_STRIPE_SECRET_KEY (sk_test_/rk_test_ only; live overrides rejected). The bot API purchase needs only that TEST secret-key access (PaymentIntents). Optional hosted Checkout also needs: CBE_SANDBOX_STRIPE_WEBHOOK_SECRET, CBE_SANDBOX_RETURN_URL (reviewed HTTPS return). POST /api/v1/sandbox/stripe/webhook verifies raw-body test signatures and retrieves exact sessions. The live Stripe client is never a fallback. - DELETE /api/v1/sandbox clears only this run, fences delayed events first, and expires active test sessions where possible. Repeat safely. Minimal closed-run security tombstones/unresolved test-session IDs and Stripe external test records are retained. Zero real charges, wallet changes, live payouts, fulfillment, reputation updates or production notifications. - Claims step (after public): POST /api/v1/products/{id}/claims {qr:"png"|"svg"| "none"} (or ?qr=) makes any missing claim links, one per unit (products:write; idempotent; never rotates links; 409 CBE_CLAIMS_NEED_PUBLIC before public). GET the same path ?status=open|locked|claimed|finished|all&qr=&page=&limit= lists units {unitSerial, status, claimToken, claimUrl, qr (PNG data URL or SVG)} plus counts and nextAsks. GET /api/v1/products/{id}/claims/export.csv gives a sheet. open = unused, locked = reserved to one buyer, claimed = buyer added a wallet (pending opt-in). Give each buyer a different unit's claimUrl; a claim token is a single-use secret. - Opt-ins: GET /api/v1/optins/pending (or /api/v1/optins?status=pending|active|all &productId=). nextAsks say " has a pending opt-in for ; approve or reject?". POST /api/v1/optins/decisions {decisions:[{optinId, decision:"approve"| "reject"}]} (1-50) returns 202 {approvalId} and emails ONE code for the batch; POST /api/v1/approvals/{approvalId}/confirm {code} applies it. Opt-in decisions ALWAYS need the emailed code, even for keys that skip product approvals. Approve starts monthly rebate payouts to the buyer's wallet (active opt-ins show payout.state scheduled, nextPeriod); reject removes the pending opt-in. - Sandbox claims (TEST ONLY): POST /api/v1/sandbox/products/{id}/claims?qr= makes test links + QR; POST /api/v1/sandbox/claims/{token}/simulate-scan {buyerName, walletAddress?} locks (no wallet) or claims (pending test opt-in) as a fake buyer; GET /api/v1/sandbox/optins; POST /api/v1/sandbox/optins/decisions returns one testCode for /api/v1/sandbox/approvals/{id}/confirm; POST /api/v1/sandbox/optins/{id}/simulate-payout records one simulated payout (livemode false). Zero live users, wallets, claims, opt-ins, emails or money. - Opt-in dashboards: GET /api/v1/optins/dashboard?role=creator|buyer|all&productId= (live key or owner session, read-only). creator.products[] has a summary line (" has N active opt-ins, $X paid this month ($Y all time)"), totals and optins[] {optinId, status pending|active|complete, buyer.displayName, basket, monthlyRebateUsd, term {totalPeriods, forever, knowable}, payouts {madeCount, paidUsd, paidByAsset, pendingCount, failedCount, remainingCount, lastPaidAt, nextPeriod, nextDueAt, overdue, thisMonth}}. buyer.optins[] are opt-ins this profile holds. Only verified payouts recorded in CBE's payout ledger count as paid; scheduled or estimated payouts never do. nextAsks: optinDashboard summary, optins[{id}].payout (overdue -> open Wallet; bots cannot send money), pendingOptins, heldOptins. Sandbox: GET /api/v1/sandbox/optins/dashboard (same shape from simulated payouts, livemode false, buyer view by fake buyer). - Reputation: GET /api/v1/reputation[/me|/{userId}]?limit=200 (live key or owner session, read-only) returns a plain 0-100 score (null = unproven until a scored event exists), level, creator and buyer breakdowns and events[] behind it. Creator: on-time, late (half credit), failed-send and missed payouts (7+ days past due, nothing recorded), opt-ins closed cleanly; median opt-in approval hours and rejections are shown, never scored. Buyer: valid claims, payouts received, working wallets, failed payouts. Computed on read from recorded events only. Other profiles come back without product/opt-in/wallet details. Anonymous summary: GET /api/public/reputation/{userId}; public product pages carry creatorReputation. nextAsks: reputationSummary, missedPayouts (self). Sandbox: GET /api/v1/sandbox/reputation (simulated events, fakeBuyers[]). - Opt-in close-out (automatic): when every payout period 1..totalPeriods has a verified payout recorded (term fixed at opt-in: ceil(price / monthly rebate)), the opt-in becomes complete, its claim unit becomes finished, its reserved yield returns to the basket (reservation = GREATEST(reserved, stock, links) - completed opt-ins), and buyer and creator each get a final receipt email. Forever opt-ins never close automatically. nextAsks: freshClaimLink ("'s opt-in for is complete... share a fresh claim link?") -> POST /api/v1/products/{id}/claims; 422 CBE_FRESH_CLAIM_CAPACITY if the basket's open yield no longer covers the unit. A buyer who completed cannot claim the same product again (409 OPTIN_ALREADY_COMPLETE). Sandbox: simulate-payout returns closeOut on the last payout (simulated receipts, zero emails). - Agent guide 3.7 (bot rough spots): simulate-payout replies (and its 409 CBE_OPTIN_COMPLETE / CBE_OPTIN_NOT_ACTIVE) carry product, stage and nextAsks (next simulatePayout, or freshClaimLink after close-out). Live payouts are sent and verified on the website, so bots read close-out from product next-asks, the dashboard and /api/v1/optins?status=complete. - Reservations: links[].reservedUnitCount = GREATEST(reserved, stock, claim links) - completed opt-ins (real units still reserved; grossReservedUnitCount and completedUnitsReturned show the parts). Basket reads (live GET /api/v1/baskets, sandbox GET /api/v1/sandbox baskets[] and POST/PATCH baskets) carry estimatedMonthlyYieldUsd, allocatedMonthlyYieldUsd and availableMonthlyYieldUsd. - Locked terms: changing links/rebates (409 CBE_LINKS_LOCKED_BY_OPTINS) or price/ quantity (409 CBE_TERMS_LOCKED_BY_OPTINS) while unfinished opt-ins hold them returns lockedFields, holders and a termsLocked ask: new_product, wait_for_close_out or keep. Terms unlock once every opt-in is complete or rejected. Live and sandbox. - Claim counts: counts.unfinished (= total - finished) and context.unfinishedUnits. - Optional answers: submit.optionalFields lists body keys to add only when the user answered (simulateBuyer walletAddress); never send placeholders. The server accepts a missing, null or empty walletAddress (lock only). - claimLinks / freshClaimLink: answer = QR format png|svg|none (default png), described in answerFields [{field:"qr"}], options[].description, suggestedDefault. - Reputation: GET /api/v1/reputation (= /me) on live and GET /api/v1/sandbox/reputation[/me] in the sandbox. Each payout is judged against its own period's due date; one paid before it is due is payout_prepaid (shown, scored once due). level stays unproven (provisional: true) until 3+ scored events span 56+ days (payouts dated by due date). Sandbox payouts use a simulated clock (period n due n months after opt-in): 3 payouts can prove a level, 1-2 cannot. - next-asks: GET /api/v1/next-asks and GET /api/v1/sandbox always include a reputationSummary ask (priority info); products with any opt-in add an optinDashboard summary (priority secondary). Same rule live and sandbox. - Agent guide 3.8 (live re-test leftovers): - Claim counts: the old alias counts.live ("unfinished", never live mode) now sits only under counts.deprecated.live, flagged in deprecatedFields, removed in 4.0. nextAsks context carries unfinishedUnits only (liveUnits removed). - availableMonthlyYieldUsd means basket-wide open yield EVERYWHERE (basket reads, product links, basket choices, capacity 422s; live and sandbox). A product link adds allocatedToOtherLinksUsd, availableForThisLinkUsd (what this link may reserve in total = basket-wide open yield + its own reservation) and maxPerItemUsd = availableForThisLinkUsd / units; capacityNotes restates them. 422 CBE_REBATE_CAPACITY: availableForThisLinkUsd + availableMonthlyYieldUsd (openMonthlyYieldUsd is a deprecated copy of availableForThisLinkUsd). 422 CBE_FRESH_CLAIM_CAPACITY: availableForThisLinkUsd + availableMonthlyYieldUsd. - One clock per mode for dashboard and reputation, returned as clock {mode, now|simulatedNow, thisMonth, rule}. Sandbox: each simulated payout is paid on its simulated due date (simulatedPaidAt); simulatedNow = later of real time and the latest simulated paid date; thisMonth counts payouts paid in simulatedNow's month. Payout entries label dates: simulatedPaidAt, dueAt, requestedAt (real request time), dates: simulated. Live: real clock (transaction time, else verification time). - Open claim links: changing a public product's links/rebates, price or other financial terms while open (unscanned) claim links exist returns 409 CBE_OPEN_CLAIM_LINKS_TERMS with changedFields, openClaimLinks and an applyToOpenClaimLinks ask: apply (resubmit with applyToOpenClaimLinks: true), retire_open_links (POST /api/v1/products/{id}/claims/retire {"retire":"open"} removes only open links; then resubmit) or keep. Same in the sandbox (/api/v1/sandbox/products/{id}/claims/retire). - Agent guide 3.9 (wording): - Locked terms name the actual holder. 409 CBE_TERMS_LOCKED_BY_OPTINS / CBE_LINKS_LOCKED_BY_OPTINS error text and the termsLocked ask say e.g. "Unit 2 is scanned and waiting for its buyer to finish" (locked claim unit) vs "2 opt-ins wait for your approve or reject decision" / "1 active opt-in still has payouts to make", with holdersByType [{type, count, unitSerials?, meaning, unlocksWhen}]. Options follow the holders: new_product, wait_for_claim, finish_test_claim (sandbox: existing simulate-scan with a wallet), review_pending, wait_for_close_out, keep. No owner or bot path releases a claim lock, so none is offered. - Plurals follow the number described: "a claim link and QR code" when one link is missing, "N claim links and QR codes, one per unit" when several. - Sandbox payout entries use one shape everywhere (simulate-payout optin and payout, opt-in lists, GET /api/v1/sandbox, dashboard history): period, amountUsd, status, requestedAt (real request time), dueAt, simulatedPaidAt, dates: "simulated". simulatedAt survives only at deprecated.simulatedAt (= requestedAt), removed in 4.0. Aliases marked "removed in 3.9" in 3.8 (counts.deprecated.live, openMonthlyYieldUsd) are kept through 3.9 and removed in 4.0. - Agent guide 4.0 (payout reminders for bots): - Bots never move money. The creator sends each payout from their own CBE payout wallet (the Base CDP wallet saved on the website); the bot only submits the transaction hash. CBE verifies it on-chain with the website's verifier: sender = the creator's CBE payout wallet, recipient = the buyer's opt-in wallet, exact amount and asset (ETH or USDC on Base), confirmed, hash unused. - payoutDueSoon ask (primary): from 3 days before each payout is due, one per opt-in: buyer, product, period N of term, amountUsd, asset {symbol, chain}, destinationWallet, dueAt; answerFields txHash + amount; submit POST /api/v1/optins/{id}/payouts. The overdue ask optins[{id}].payout is primary too and carries the same details and submit path (context.url = Wallet). - Statuses: scheduled, due_soon (within 3 days), due (within 24 hours), overdue, verifying (recorded, waiting for confirmations). - Calendar: GET /api/v1/payouts/upcoming?days=30&role=creator|buyer|all (days 1-90, default 30; overdue always included). creator.payouts[] are payouts you owe (with submit); buyer.payouts[] are payouts owed to you. GET /api/v1/payouts/upcoming.ics = the same as an iCalendar feed. - Submit: POST /api/v1/optins/{id}/payouts {"period":1,"txHash":"0x...", "amount":"1.5","chain":"base","asset":"USDC"} (products:write; your own opt-in). amount = exact coin amount sent, worth at least the agreed USD rebate at spot less 2%. 201 verified, 200 idempotent repeat, 202 pending (resubmit to recheck). A failed check (422 CBE_PAYOUT_VERIFY_FAILED) stores nothing. Reused hash 409 CBE_PAYOUT_TX_REUSED; paid period 409 CBE_PAYOUT_PERIOD_PAID; no payout wallet 409 CBE_PAYOUT_WALLET_REQUIRED. Replies carry nextAsks: the next period (payoutScheduled) or, after the last one, close-out freshClaimLink. No emailed code: recording a sent payout moves no money (the website records a sent hash without a code too); the on-chain check is the proof. - Reminder emails: an hourly sweep emails the creator once when a payout enters the 3-day window and once when it becomes overdue; never resent. - Sandbox: GET /api/v1/sandbox/payouts/upcoming (+ .ics) on the simulated clock with simulatedReminders[] (email false, never sent). POST /api/v1/sandbox/clock {"advanceDays":n} or {"advanceTo":"due_soon"|"due"| "overdue"} moves the clock forward. POST /api/v1/sandbox/optins/{id}/payouts takes any fake 0x+64-hex txHash (ending in dead = simulated failed check). - Removed (deprecated in 3.8/3.9, as announced): counts.deprecated.live, deprecatedFields, openMonthlyYieldUsd and sandbox payouts[].deprecated .simulatedAt. Read counts.unfinished, availableForThisLinkUsd, requestedAt. Sandbox payout entries add txHash and source. - Agent guide 4.1 (opt-in approval reminders): - A pending opt-in (buyer scanned the QR and added a wallet) turns active only after you approve with the emailed code. Pending opt-ins now carry waitingSince, waitingHours, waitingText ("2 days"), urgent (24h+) and reminders {approval_24h, approval_72h} (GET /api/v1/optins/pending, decision asks, calendar). - optins[{id}].decision stays primary with the batch combinedSubmit (one emailed code). From 24h it reads "Ada has waited 2 days for your approval on Rog'Ver Wastor; approve or reject?" and is sorted first (oldest first) in product and account next-asks; only a pending code ask goes before it. - One poll place: GET /api/v1/payouts/upcoming (creator view) also lists creator.pendingApprovals[] (oldest first; awaitingCode when a decision was already requested) and approvalTotals {count, urgent, oldestWaitingHours}, and nextAsks carry the decision asks. An approval starts the payout stream. - Reminder emails: the hourly sweep emails the creator once at 24h and once more at 72h per pending opt-in (first seen past 72h: only the 72h email), batched into one email per creator per sweep: buyer, product, how long waiting, and how to approve (website Products > Pending Approvals, or ask your bot; the decision still needs the emailed code). Never resent; failed sends retried up to 3 attempts. - Sandbox: same asks, ordering and pendingApprovals on the simulated clock; simulatedReminders[] adds kind approval_24h|approval_72h and simulatedApprovalEmails[] groups one simulated email per sweep (never sent). POST /api/v1/sandbox/clock {"advanceDays":1} tries it. - Agent guide 4.2 (quota status, site-owner exemption, sandbox fixes): - Live /api/v1 calls are metered: 100 per UTC month on the free tier, then 402 "Free quota exceeded" (body adds quota {...}); warning email at 80. Metered responses carry X-CBE-Quota-Limit and X-CBE-Quota-Remaining. GET /api/v1/usage (counted like any call) returns quota {exempt, monthlyLimit, remainingCalls, callCount, subscribed}. - Site-owner keys are exempt: quota {exempt: true, reason: "site_owner", via: "admin_role"|"env_allowlist"} and header X-CBE-Quota-Exempt: site_owner. Site owner = the platform admin role or a user id in the server's CBE_QUOTA_EXEMPT_USER_IDS secret; nothing a caller sends can make a key exempt. Exempt calls are counted and logged, never blocked, and never trigger the warning email. - Overdue payout ask renamed to match payoutDueSoon: field payoutOverdue, id optin:{id}:payout-overdue:{period}. The 4.1 names (optins[{id}].payout, optin:{id}:overdue) stay in deprecatedAliases (kept one extra release through 4.3; removed in 4.4). - Sandbox: approval reminder simulatedSentAt is the mark itself (stable across reads) with simulatedSweepAt for the hourly sweep; close-out completedAt is on the simulated clock (dates "simulated", recordedAt real); payout items carry creator.displayName "TEST owner". - Agent guide 4.3 (Bots for Business): - Bots for Business: custom Grok Bot builds for local businesses (customer-reply bot, inventory/listing bot, CBE rewards bot that runs QR claims, approvals and payouts, and open-ended bot for any task the business describes). Website: /bots (landing, /bots/packages, /bots/samples, /bots/book). Packages show no price unless the owner set one. - Book for a business (public, no key), one question at a time: GET /api/v1/bots-for-business -> ask bfb:meeting-type (phone | video | in_person; in_person only once the owner sets an address) -> GET /api/v1/bots-for-business/availability?type=&from=YYYY-MM-DD&days=N (owner timezone, default America/New_York) -> ask bfb:slot -> ask bfb:details (businessName, contactName, email, phone, description, optional packageId) -> POST /api/v1/bots-for-business/bookings. packageId open-ended = any task; put the task in description (catalog jobTypes lists the job type choices). - A slot never books twice (409 CBE_BFB_SLOT_TAKEN with alternatives). Limits: 3 bookings/day per email, 5/day per IP, 20 attempts/10 min per IP (429 CBE_BFB_RATE_LIMIT + Retry-After). leaveBlank is a honeypot: omit it. - The 201 reply carries manage {url, token}; the business gets an email with meeting.ics. POST /bookings/{id}/reschedule {token, startsAt} or /bookings/{id}/cancel {token}; GET /bookings/{id}/invite.ics?token=. - Site owner only (admin role or CBE_QUOTA_EXEMPT_USER_IDS; key or session): GET/PATCH /api/v1/bots-for-business/settings, GET /owner/bookings, POST /owner/bookings/{id}/cancel, POST /owner/bookings/mark-reviewed, GET /owner/next-asks (review new bookings, set hours, alert email, in-person address, video link). - Product publication and application deployment are separate permissions. These upgrades do not authorize publishing, production migrations or live provider configuration. Read /api/v1/agent-guide and canonical OpenAPI. ## CBE creator flow — any bot or AI agent ### Sandbox contracts and diagnostics - An empty GET /api/v1/sandbox offers newBasket with required explicit name and monthlyYieldUsd, and newProduct choices: Etsy/eBay listing URL or manual name. Use returned answerFields and each choice's submit body. Cleanup is secondary. - Run stage: any simulatedPublic product makes the run public; otherwise awaiting_approval > draft > needs_basket > needs_rebate > ready. Empty is draft. Each product retains its own stage. Simulated public stays outside live discovery. - GET /api/v1/sandbox/approvals/{id} returns status, sentAt, deliveredAt, expiresAt, remainingTries and deliveryError before/after confirmation and wrong attempts, expiry or exhaustion. TEST ONLY approvals send zero mail: delivery timestamps stay null. Status never returns codes or hashes. Confirmed precedes expiry; otherwise expired precedes exhausted. - HTTP 422 CBE_PUBLICATION_INCOMPLETE includes missingRequirements (including imageUri) and candidate-aware nextAsks. HTTP 422 CBE_REBATE_CAPACITY in live and sandbox includes capacity: basketId, availableForThisLinkUsd, availableMonthlyYieldUsd, openMonthlyYieldUsd (deprecated copy), currentReservedUnitCount, proposedReservedUnitCount, maxMonthlyRebatePerItemUsd (floored to eight decimals), plus corrective nextAsks. Error envelope: {error, code?, missingRequirements?, capacity?, nextAsks?}. - Listing requestId is optional by omission only. Supplied UUIDs must be valid: empty, malformed, null or non-string values return HTTP 400 before fetch/provider effects. Clone-and-link and buy require requestId. Same-input replay is idempotent; conflicting reuse returns HTTP 409. - Bounded JSON-LD/Open Graph import always creates a private draft. import includes readable, reason, issues and message. Reasons: provider_denied, timeout, unsafe_url, network_error, read_limit, unreadable_metadata, ambiguous_metadata, ambiguous_price, non_usd_price. Only confirmed denial says Etsy/eBay prevented CBE from reading the page. Missing-field nextAsks explain the failure. Zero cookies, anti-bot circumvention, guessed prices or automatic conversion. - GET /api/v1/sandbox/readiness returns secret-free configuration checks. Use a separate Stripe TEST connection or CBE_SANDBOX_STRIPE_SECRET_KEY (sk_test_/rk_test_ only). apiPurchase.ready needs only those credentials. Optional hosted Checkout also needs dedicated CBE_SANDBOX_STRIPE_WEBHOOK_SECRET and an owner-reviewed HTTPS /sandbox/return URL. Configuration checks alone prove neither provider permissions nor webhook registration nor a paid hosted session. - Owner setup: enter keys/signing secrets only in Replit Secrets, never chat or logs. Bot API purchase: verify TEST PaymentIntents create/read. Optional hosted Checkout only: verify Products/Prices create, Checkout Sessions create/read/expire permissions. Register /api/v1/sandbox/stripe/webhook in Stripe TEST mode for checkout.session.completed, checkout.session.async_payment_succeeded and checkout.session.expired; store that endpoint's dedicated signing secret. Development uses its preview return URL. Later owner-reviewed production setting: CBE_SANDBOX_RETURN_URL=https://cryptobackedeverything.com/sandbox/return. Zero live fallback; missing access/configuration and live keys fail closed. This guidance authorizes zero production configuration or publishing. JSON guide with request/response samples: /api/v1/agent-guide Creator-wide next questions: GET /api/v1/next-asks One product's questions: GET /api/v1/products/{id}/next-asks Root /llms.txt and /api/llms.txt serve this same document. Follow nextAsks, preserving facts and limits when asking in the user's own words. NEVER guess amounts or pick baskets for the user. Each ask provides id, stage, field, question, context, type, limits, units, options, submission endpoint, HTTP method, JSON body template and answer binding, plus approval requirements. Types: text, amount, number, choice, image, url, open_link, code. When a choice option has its own submit metadata, use that option's endpoint/body. After cloning, follow the returned asks before submitting the subsequent link action. Product stages: draft, needs_basket, needs_rebate, awaiting_approval, ready, public. ready is PRIVATE. Only an explicit publication choice makes a product public. 1. ACCOUNT: Use X-API-Key or an owner session Bearer. A key belongs to the profile that owns it; another profile cannot edit its products. For in-chat CBE access, POST /api/v1/auth/start {"email":"user@example.com","keyName":"Agent name"}. Response: challengeId, stage account, nextAsks for the emailed code. POST /api/v1/auth/verify {"challengeId":"returned id","code":"user's code"}. Response: one-time scoped apiKey and creator nextAsks. Securely retain the key. Access codes: 10 minutes, five wrong tries, five requests/email/hour and twenty/IP/hour. This is CBE access, never the separate /api/ms/v1 signup. 2. CREATE: POST /api/v1/products {"name":"User's title"} creates a PRIVATE draft. Response: product, stage draft and asks for missing description, USD price, photo URL, stock quantity and source URL. Imported values are preserved. PATCH the specified endpoint with the user's missing answers. Combine compatible answers rather than repeatedly proposing already answered fields. 3. BIND: The basket question lists creator-owned baskets with estimated monthly yield, already committed yield and linked-product counts. Ask whether to link or clone each named basket. Link via POST /api/v1/products/{id}/links {"basketId":"user-selected id"}. Atomic clone-and-link via POST /api/v1/products/{id}/clone-and-link {"basketId":"user-selected source id","requestId":"caller UUID"}. The approved operation links the copy directly. Copies duplicate zero real funds or spendable yield. For a new/funded basket, follow open_link to /baskets and have the person select New basket in THEIR browser. Refresh GET /api/v1/next-asks after they return. 4. REBATE: Ask “How much of [basket]'s monthly yield should go to [product]'s monthly rebate payment, per item sold?” The context gives open yield, reserved unit quantity and maximum per item. PATCH /api/v1/products/{id}/links/{basketId} {"monthlyRebateUsd":"replace this template with the user's NUMERIC answer"}. One canonical amount per link, USD/month PER SOLD ITEM. It must be positive, and amount times reserved quantity must fit open yield, including at confirmation. 5. APPROVAL: Key PATCH normally returns 202, approvalId, awaiting_approval and the next code ask. POST /api/v1/approvals/{approvalId}/confirm {"code":"user's six-digit code"}. Approval codes: 15 minutes, five wrong tries, ten requests per MERCHANT per hour across all their keys. Requests together may arrive in one email after about one minute, each with an independent code. Remind people to check spam and mark CBE mail as “Not spam”. Never retrieve sent-message codes from the provider to impersonate owner consent. 6. GO PUBLIC: At ready, ask whether the user wants the product public. PATCH /api/v1/products/{id} {"publicationDecision":"publish"}. Follow its approval code ask when required. Confirmation returns public stage. For private, send {"publicationDecision":"private"} and respect that choice. Completed decisions have nextAsks: []. Incomplete/ready previews stay private. Keep promised customer payments protected. Product publication is different from publishing or republishing the CBE application. > Crypto-collateralized everyday products. Merchants back physical or digital goods with a crypto yield basket. Customers opt in and receive scheduled crypto payouts. Merchants keep ownership of collateral. Website: https://cryptobackedeverything.com API base: https://cryptobackedeverything.com/api OpenAPI: https://cryptobackedeverything.com/api/openapi.yaml OpenAPI (JSON): https://cryptobackedeverything.com/api/openapi.json Health: https://cryptobackedeverything.com/api/healthz MCP: https://cryptobackedeverything.com/api/mcp Bot connection guide: https://cryptobackedeverything.com/bot-connections.md OAuth discovery: https://cryptobackedeverything.com/.well-known/oauth-authorization-server Device endpoint: https://cryptobackedeverything.com/oauth/device/code Token endpoint: https://cryptobackedeverything.com/oauth/token Delegated marketplace scopes: items:read offers:write sell:write pay profile:read. User-delegated credentials and store keys are both authenticated, not equally authorized. Native Grok device-grant support is unverified; read the guide before claiming compatibility. ## For AI agents 1. Read the OpenAPI spec first. Do not guess routes. 2. Public, no auth: - GET /api/healthz - GET /api/public/backings - GET /api/public/backings/{id} - GET /api/staking-rates - GET /api/claim/{token}/info 3. Developer / agent API is under /api/v1/ and requires auth: - Authorization: Bearer - or X-Api-Key: 4. Create an API key as a signed-in merchant: POST /api/developer/keys 5. v1 is metered: 100 free calls/month, then HTTP 402 with callCount and monthlyLimit. 6. MCP clients can connect to POST /api/mcp using Streamable HTTP and the same credentials. 7. MCP is intentionally read-only: list public backings, your products, and your baskets. 8. MCP reads are unlimited for 30 days from the first successful tool call. After that, an active $5/month developer subscription is required. 9. There is no ChatGPT plugin runtime. 10. Product results include agentProductCard schemaVersion 1.0 and a stored paybackContract. Use the saved duration, exact total, regular-payment count, and final payment directly; never multiply the monthly amount by the rounded-up month count. Check dataCompleteness and do not invent missing values. ## Product objects - Product: crypto-backed item (inventoryType: one | many | nft_collection) - paybackContract: authoritative saved payback terms; finite totals equal the item price and the final payment may be smaller - agentProductCard: normalized commerce presentation for AI shopping agents; existing product fields remain for compatibility - Basket: yield basket (basketType: pos | pow) that funds payouts - Opt-in: customer enrollment (pending | approved | rejected) - Claim token: QR code that maps one unit to a customer ## Do not - Do not scrape the React homepage for API truth. The HTML is an empty #root. - Read /llms.txt or /api/llms.txt as plain text; follow the documented JSON submission endpoints. - Do not store Coinbase private keys in agent memory or logs. - Do not identify people from product imagery. - Do not describe stated payout terms as guaranteed returns or calculate speculative ROI. ## Marketplace Storage partner stores - Store applications, owner login and invited-staff login are separate from buyer/seller signup. Use the store portal at /marketplace/store or the store operations in /openapi.json. - Store clients are cbe-ms-store-web and cbe-ms-store-bot, with stores:access only. Existing buyer/seller clients and default permissions are unchanged. - Applicants cannot operate or approve a store. Only platform administrators approve an application after acceptance of an approved, published agreement. - Owner and staff operations are bound to one verified location. Internal adapter keys must never be returned to users or bots. - Read GET /api/ms/v1/items/{id}/policy before checkout for a managed item. Its immutable policy may differ from the location's newer agreement. - Managed inventory snapshots rates at physical check-in; holding starts immediately and repeats at each started 30-day period through pickup. Estimates and final settlements are separate. - Legal wording requires creator approval. Drafts cannot be accepted or activated; no automatic disposal, refunds, monthly card debits or commission transfers are authorized.