# Qart > Bitcoin commerce toolkit. Open source. Federated. LLM-native. Qart is an Elixir/Phoenix LiveView application that provides P2P Bitcoin (BSV) commerce infrastructure. It is a toolkit — the packaged app at qart.app is one expression of the toolkit, not the product itself. ## Start here A small business can start with what it knows (sales, receipts, books its accountant can read) and climb, one rung at a time, to bitcoin-native commerce: selling online, loyalty and stored value, pay-per-request (HTTP 402), and smart contracts. The page for people is `/start`. The same ladder as JSON is `GET /api/v1/capabilities`. The owner's Qart token is a user API key (made at `/settings`). Presented as `Authorization: Bearer ` to `GET /api/v1/me`, it returns their shops, their role in each, the endpoints that role may call, and where each shop stands on the ladder with its next step. When this file is served by a Qart instance at `/llms.txt`, three generated sections follow it: the ladder in full, how an assistant should work with a shop owner, and every endpoint. ## What Qart Does - Merchants list items and accept BSV payments via QR code checkout - On-chain receipts, provenance, and loyalty stamps using MAP-formatted OP_RETURN protocol - Federation: Qart instances discover each other via peer exchange, Atom feeds, and on-chain announcements - LLM-powered site builder generates static commerce sites; an ongoing coach helps merchants iterate - Commerce API lets any static site (Jekyll, HTML, anything) accept Bitcoin payments via `qart-checkout.js` ## Architecture Elixir/Phoenix backend. PostgreSQL. BSV blockchain via WhatsOnChain API. ### Core Contexts - **Accounts** — users, auth, wallets, API keys - **Organizations** — merchants, memberships, payment config, webhook URLs, Commerce API keys - **Orders** — order lifecycle (pending → paid → preparing → ready → completed), payment verification - **Inventory** — items, catalog, pricing in sats - **Protocol** — MAP-formatted on-chain event types: catalog_entry_v1, order_v1, receipt_v1, proof_v1, peer_v1, tax_v1 - **Federation** — peer exchange, crawl worker, Atom feed aggregation, instance identity (/.well-known/qart.json) - **Sites** — LLM site builder, coach, GitHub integration, fragment renderer - **Payments** — shared verification module, org wallet funding ### Key APIs **Commerce API** (`/api/v1/commerce/:merchant_handle/`) - `GET /catalog` — paginated items - `POST /checkout` — create order (supports item IDs or ad-hoc items by name + price_sats) - `GET /checkout/:token/status` — payment status **Discovery and whoami** - `GET /api/v1` — every endpoint, generated from the router - `GET /api/v1/capabilities` — the small-business ladder (`Qart.Capabilities`) - `GET /api/v1/me` — with a user API key: shops, roles, endpoints, and each shop's ladder - `GET /api/v1/orgs/:handle/capabilities` — one shop's ladder with progress (staff) **Books** (`GET /api/v1/orgs/:handle/ledger`, admin) - A balanced double-entry journal of paid orders: chart of accounts, entries, trial balance - `?format=csv` for one row per line; `from` and `to` narrow the range - Each entry carries the payment txid and receipt txid: triple-entry accounting **Merchant Onboarding** (`/api/v1/onboard`) - `POST` with `Authorization: Bearer ` — creates org, generates Commerce API key, sets CORS origins **Federation** (`/api/v1/peers/`) - `GET /` — this node's known peers - `POST /exchange` — merge peer lists between nodes **Instance Identity** (`/.well-known/qart.json`) - Protocol version, capabilities, merchant/peer counts, endpoint URLs ### Client JS **qart-checkout.js** — in-page Bitcoin checkout for static sites. Add a `
``` **qart-tx.js** — HTML markup ↔ Bitcoin transaction mapping. `[data-qart-tx]` elements with `data-to`, `data-sats`, `data-memo`. ## On-Chain Protocol All commerce events use MAP (Magic Attribute Protocol) format in OP_RETURN: ``` OP_FALSE OP_RETURN "1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5" "SET" "app" "qart" "type" "" ``` Event types: `catalog_entry_v1`, `order_v1`, `receipt_v1`, `proof_v1`, `peer_v1`, `tax_v1`. Any MAP-aware indexer can discover Qart events by querying `app = "qart"`. See `docs/PROTOCOL.md` for full field definitions. ## Federation Qart instances form a network with no center. qart.app is a bootstrap node (qatalyst), not the authority. - **Peer exchange**: nodes share peer lists via `/api/v1/peers/exchange` - **Feed aggregation**: approved Atom feeds (a peer's `/marketplace/feed.atom`, or a static site's Atom file) are fetched and their items shown in the marketplace Federated tab. Feeds from peers, the crawler, and `POST /api/v1/feeds` are proposals until a site admin approves them at `/admin/feeds`; only boot peers and configured trusted hosts start approved - **On-chain announcements**: OP_RETURN `peer_v1` events make nodes discoverable via the blockchain - **Instance identity**: `/.well-known/qart.json` describes capabilities so peers can verify before federating Running behavior: `docs/MULTI_INSTANCE.md` § Federated Feeds. Design contract: `docs/SYNDICATION.md`. Original vision: `docs/explorations/QART_FEDERATION.md`. ## Multi-Instance Deployment Any merchant can fork Qart, deploy on AWS, and become discoverable. See `docs/MULTI_INSTANCE.md`. Key env vars: `PHX_HOST`, `BSV_NETWORK`, `INSTANCE_NAME`, `FEDERATION_BOOT_PEERS`, `DATABASE_URL`, `SECRET_KEY_BASE`, `VAULT_KEY`. ## BSV Network Set via `BSV_NETWORK` env var. Defaults to `test`. Network config is resolved once via `WhatsOnChain.network/0` — all WoC API calls, explorer URLs, and BIP21 URIs use this single source of truth. ## Authentication - **Browser**: `phx.gen.auth` sessions + HandCash OAuth - **API**: `Authorization: Bearer ` — keys created via `Qart.Analytics.create_api_key/2`, validated via `ApiAuth` plug - **Commerce API**: `x-commerce-key` header with per-org public key - **Keypair Auth**: challenge-response BSV keypair authentication for external apps ## Tech Stack - Elixir 1.18+ / Phoenix 1.8 / LiveView - PostgreSQL (RDS in prod) - HTTP clients: Tesla (Mint adapter) in most modules, including WhatsOnChain and federation; Req in a few (`Qart.AI`, the 1Sat ordinals client, the token minter) - BSV library (`bsv-ex`) for transaction building - Tailwind CSS - Deployed as Docker containers on AWS (ECR + EC2/ALB or App Runner) ## Source https://github.com/afomi/qart ## For a small business: what Qart can do, in order Start where the shop is. Each rung works on its own; nobody has to climb them all. Maturity: live (ready to use), testnet (built and tested; the mainnet walk-through is still owed), preview (built, with the known gap named). Pages use `:handle` for the shop's handle. The same ladder as JSON: https://qart.app/api/v1/capabilities ### 1. Keep the books Every sale lands in a standard journal your accountant can import. - **Double-entry journal** — Each paid order becomes a balanced journal entry: money in, revenue, sales tax owed. Export it as CSV for QuickBooks, Xero or your accountant. (live; page https://qart.app/orgs/:handle/reports; API `GET /api/v1/orgs/:handle/ledger`) - **Sales tax schedules** — Pick your jurisdiction once. Orders carry the right rate, and the tax collected is tracked as a liability. (live; page https://qart.app/orgs/:handle/tax-dashboard) - **Receipts that prove themselves** — A receipt can be written to the chain, so anyone can verify a purchase from its txid on any Qart instance. The receipt is the transaction: the third entry beside your debits and credits. (live; page https://qart.app/orgs/:handle/settings) - **Sales reports** — Daily and monthly sales, top items and busy hours, without a spreadsheet. (live; page https://qart.app/orgs/:handle/reports) ### 2. Get paid in person Take a payment at the counter today, with the phone you already have. - **Your own receiving address** — Payments go straight to your wallet. Qart never holds your money and never holds your keys. (live; page https://qart.app/orgs/:handle/settings; API `GET /api/v1/organizations`, `POST /api/v1/onboard`, `POST /api/v1/organizations`, `GET /api/v1/organizations/:handle`) - **Point of sale** — A menu, a cart and a QR code on a tablet or phone. The customer scans, pays, and you see it land. (live; page https://qart.app/orgs/:handle/pos; API `GET /api/pos/orders/:order_number`, `POST /api/pos/orders`, `POST /api/pos/orders/:order_number/confirm`, `POST /api/v1/orgs/:handle/items`, `GET /api/v1/orgs/:handle/items`, `GET /api/v1/orgs/:handle/menu`, `PUT /api/v1/orgs/:handle/menu`) - **Pay links and tips** — A link or QR anyone can pay, for an amount you set or one they choose. (live; page https://qart.app/pay/:handle) - **Cards and other wallets** — Connect Stripe or HandCash so customers without bitcoin can still pay you; it all lands in the same books. (live; page https://qart.app/orgs/:handle/settings) ### 3. Sell online Put a Buy button on any website, or let Qart build the site with you. - **Checkout on your own site** — One script tag turns any page into a store: a Buy button, an inline QR and a confirmation, no redirect. (live; page https://qart.app/orgs/:handle/settings; API `GET /api/v1/commerce/:merchant_handle`, `GET /api/v1/commerce/:merchant_handle/catalog`, `GET /api/v1/commerce/:merchant_handle/catalog/:id`, `POST /api/v1/commerce/:merchant_handle/checkout`, `GET /api/v1/commerce/:merchant_handle/checkout/:public_token/status`, `GET /api/v1/commerce/:merchant_handle/tickets`, `POST /api/v1/commerce/:merchant_handle/tickets/checkout`) - **A site, built with you** — Describe your shop and get an editable website on GitHub Pages, with a coach that keeps helping after launch. (live; page https://qart.app/sites/new) - **The Open sign** — Tell neighbors you are open right now, on Qart and on your own site, and answer questions while they browse. (live; page https://qart.app/orgs/:handle/counter; API `GET /api/v1/shops/:org_handle/sign`) - **Found across the network** — Your catalog is an open feed. Other Qart instances can list it, with no platform in the middle. (live; page https://qart.app/marketplace; API `GET /api/v1/peers`, `POST /api/v1/peers/exchange`, `GET /api/v1/feeds`, `POST /api/v1/feeds`, `GET /api/v1/discover/nearby`, `GET /api/v1/discover/search`, `GET /api/v1/discover/featured`, `GET /api/v1/discover/categories`, `GET /api/v1/discover/categories/:category`, `GET /api/v1/discover/merchants/:merchant_handle`) ### 4. Bitcoin-native economics Rewards, stored value and stable money that customers actually own. - **Loyalty stamps** — Stamp cards that live in the customer's wallet, optionally as on-chain stamps, so the reward is theirs. (live; page https://qart.app/pos/cards; API `GET /api/pos/loyalty-cards/lookup/:identifier`, `GET /api/pos/loyalty-cards/:id`, `POST /api/pos/loyalty-cards`, `POST /api/pos/loyalty-cards/:id/stamp`, `POST /api/pos/loyalty-cards/:id/redeem`, `POST /api/pos/loyalty-cards/:id/transfer`, `POST /api/v1/loyalty-cards`) - **Prepaid and gift cards** — Sell stored value up front. Redemptions draw down a liability in your books, not a mystery balance. (live; page https://qart.app/orgs/:handle/prepaid-cards; API `GET /api/pos/prepaid-cards/lookup/:identifier`, `GET /api/pos/prepaid-cards/:id`, `POST /api/pos/prepaid-cards`, `POST /api/pos/prepaid-cards/:id/use`, `POST /api/pos/prepaid-cards/:id/transfer`, `POST /api/v1/prepaid-cards/purchase`) - **Stable value (MNEE)** — Price and settle in a dollar stablecoin on the same chain when you would rather not hold bitcoin. (testnet; API `POST /api/v1/mnee/intents`, `GET /api/v1/mnee/intents/:payment_id`, `POST /api/v1/mnee/intents/:payment_id/settle`) - **Your own token** — Mint a BSV-21 token for memberships, credits or a community currency. (preview; page https://qart.app/tokens/mint; The coordinated testnet run with the bitblocks indexer is still owed (#10).) ### 5. Peer to peer and 402 Get paid per request, per article, per API call, directly, with no middleman. - **402 Payment Required** — Put a price on a URL. A request without payment gets HTTP 402 and a payment request; a paid one gets through. Micropayments of a few sats work. (live; page https://qart.app/402; API `GET /api/v1/402/resources`, `POST /api/v1/402/resources`, `DELETE /api/v1/402/resources/:id`, `POST /api/v1/402/check`, `POST /api/v1/402/verify`, `GET /api/v1/402/payments`) - **A payment name** — Customers pay a name like you@qart.app instead of copying a long address. (live; API `GET /api/v1/bsvalias/id/:paymail`, `GET /api/v1/bsvalias/address/:paymail`, `POST /api/v1/bsvalias/address/:paymail`, `GET /api/v1/bsvalias/public-profile/:paymail`, `GET /api/v1/bsvalias/verifypubkey/:paymail/:pubkey`) - **Sign in with a key** — Your identity is a key you hold (did:key), recognized by any app that checks it. No password to leak. (live; page https://qart.app/identity/link; API `POST /api/auth/challenge`, `POST /api/auth/verify`) - **Wallet API** — List spendable coins and broadcast transactions your own tools signed. The key stays with you. (live; API `GET /api/v1/wallet/utxos`, `POST /api/v1/wallet/broadcast`) ### 6. Smart contracts Agreements that settle themselves: the terms are in the transaction. - **Escrow** — Buyer, seller and an arbiter share a 2-of-3 lock. Two of three agree and the money moves; no platform decides. (testnet; page https://qart.app/orders; The testnet walk-through is still owed (#38).) - **Pre-signed refunds** — The refund is signed before the money moves, locked until a date. The customer is protected without trusting you. (testnet) - **Referral covenants** — Pay a referrer automatically when their link sells, enforced by the script instead of a promise. (preview; page https://qart.app/referrals; The covenant pays to P2SH, which is disabled; it moves to a bare script first (#60).) - **Co-signed transactions** — Build a transaction here, sign it in your own wallet, combine signatures from partners. Keys never leave their owners. (testnet; page https://qart.app/wallet/tx) ## If you are an assistant helping a shop owner 1. Ask the owner for a user API key, made at https://qart.app/settings. It is their Qart token: it acts as them, with their role in each shop. Treat it like a password. 2. `GET https://qart.app/api/v1/me` with `Authorization: Bearer `. It lists their shops, their role in each, the endpoints that role may call, and each shop's `ladder` with `next`: the suggested next step. 3. No shop yet? `POST https://qart.app/api/v1/organizations`, or send the owner to https://qart.app/setup, a guided setup with a coach that ends in a real test payment. 4. Walk the ladder from `ladder.next`. Explain each step in the owner's terms (the `for_you` text), not in protocol terms. 5. Anything that moves money or writes to the chain: propose it and let the owner confirm. Qart never holds their keys; transactions are signed in their wallet. 6. Their books: `GET https://qart.app/api/v1/orgs/:handle/ledger?format=csv` is a balanced double-entry journal, one row per line, with the payment txid beside each entry. ## Every endpoint Generated from the router; the same list as JSON is `GET /api/v1`. Authentication: - none: No credentials. - bearer: Authorization: Bearer . A key acts as its user, with that user's org roles. Create one at /settings. - webhook: Signed by the provider (Stripe, HandCash). - commerce_key: x-commerce-key: . Publishable, not a secret. - bearer_optional: Authorization: Bearer , optional at the pipeline. Endpoints that act on a user return 401 without one. - paygate_key: Authorization: Bearer . Not a user API key. - `POST /api/webhooks/stripe` (webhook) - `POST /api/webhooks/handcash` (webhook) - `GET /api/v1/bsvalias/id/:paymail` (none) - `GET /api/v1/bsvalias/address/:paymail` (none) - `POST /api/v1/bsvalias/address/:paymail` (none) - `GET /api/v1/bsvalias/public-profile/:paymail` (none) - `GET /api/v1/bsvalias/verifypubkey/:paymail/:pubkey` (none) - `GET /api/pos/cards/:card_id` (none) - `GET /api/pos/cards/address/:address` (none) - `GET /api/pos/prepaid-cards/lookup/:identifier` (none) - `GET /api/pos/prepaid-cards/:id` (none) - `GET /api/pos/loyalty-cards/lookup/:identifier` (none) - `GET /api/pos/loyalty-cards/:id` (none) - `GET /api/pos/orders/:order_number` (none) - `POST /api/pos/cards` (bearer, staff role) - `GET /api/pos/shops/:shop_id/cards` (bearer, staff role) - `POST /api/pos/prepaid-cards` (bearer, staff role) - `POST /api/pos/prepaid-cards/:id/use` (bearer, staff role) - `POST /api/pos/prepaid-cards/:id/transfer` (bearer, staff role) - `POST /api/pos/loyalty-cards` (bearer, staff role) - `POST /api/pos/loyalty-cards/:id/stamp` (bearer, staff role) - `POST /api/pos/loyalty-cards/:id/redeem` (bearer, staff role) - `POST /api/pos/loyalty-cards/:id/transfer` (bearer, staff role) - `POST /api/pos/orders` (bearer, staff role) - `POST /api/pos/orders/:order_number/confirm` (bearer, staff role) - `POST /api/auth/challenge` (none) - `POST /api/auth/verify` (none) - `GET /api/v1` (none) - `GET /api/v1/capabilities` (none) - `GET /api/v1/402/resources` (paygate_key) - `POST /api/v1/402/resources` (paygate_key) - `DELETE /api/v1/402/resources/:id` (paygate_key) - `POST /api/v1/402/check` (paygate_key) - `POST /api/v1/402/verify` (paygate_key) - `GET /api/v1/402/payments` (paygate_key) - `GET /api/v1/me` (bearer) - `GET /api/v1/organizations` (bearer) - `GET /api/v1/wallet/prepaid-cards` (bearer) - `GET /api/v1/wallet/loyalty-cards` (bearer) - `POST /api/v1/prepaid-cards/purchase` (bearer) - `POST /api/v1/loyalty-cards` (bearer) - `POST /api/v1/onboard` (bearer_optional) - `POST /api/v1/organizations` (bearer_optional) - `GET /api/v1/organizations/:handle` (bearer, staff role) - `POST /api/v1/orgs/:handle/items` (bearer, staff role) - `GET /api/v1/orgs/:handle/items` (bearer, staff role) - `GET /api/v1/orgs/:handle/capabilities` (bearer, staff role) - `GET /api/v1/orgs/:handle/ledger` (bearer, admin role) - `GET /api/v1/peers` (none) - `POST /api/v1/peers/exchange` (none) - `GET /api/v1/feeds` (bearer_optional) - `POST /api/v1/feeds` (bearer_optional) - `GET /api/v1/shops/:org_handle/sign` (none) - `GET /api/v1/commerce/:merchant_handle` (commerce_key) - `GET /api/v1/commerce/:merchant_handle/catalog` (commerce_key) - `GET /api/v1/commerce/:merchant_handle/catalog/:id` (commerce_key) - `POST /api/v1/commerce/:merchant_handle/checkout` (commerce_key) - `GET /api/v1/commerce/:merchant_handle/checkout/:public_token/status` (commerce_key) - `GET /api/v1/commerce/:merchant_handle/tickets` (commerce_key) - `POST /api/v1/commerce/:merchant_handle/tickets/checkout` (commerce_key) - `GET /api/v1/discover/nearby` (bearer_optional) - `GET /api/v1/discover/search` (bearer_optional) - `GET /api/v1/discover/featured` (bearer_optional) - `GET /api/v1/discover/categories` (bearer_optional) - `GET /api/v1/discover/categories/:category` (bearer_optional) - `GET /api/v1/discover/merchants/:merchant_handle` (bearer_optional) - `GET /api/v1/passes/coffee/:card_id/apple` (bearer_optional) - `GET /api/v1/passes/coffee/:card_id/google` (bearer_optional) - `POST /api/v1/items/analyze` (bearer_optional) - `POST /api/v1/items` (bearer_optional) - `GET /api/v1/orgs/:handle/menu` (bearer, staff role) - `PUT /api/v1/orgs/:handle/menu` (bearer, staff role) - `PUT /api/v1/orgs/:handle/tickets/catalog` (bearer, admin role) - `GET /api/v1/orgs/:handle/tickets/orders` (bearer, admin role) - `POST /api/v1/pins` (bearer_optional) - `GET /api/v1/pins/nearby` (bearer_optional) - `GET /api/v1/wallet/utxos` (bearer_optional) - `POST /api/v1/wallet/broadcast` (bearer_optional) - `POST /api/v1/mnee/intents` (bearer_optional) - `GET /api/v1/mnee/intents/:payment_id` (bearer_optional) - `POST /api/v1/mnee/intents/:payment_id/settle` (bearer_optional) - `GET /api/v1/bsv/*path` (bearer_optional) - `POST /api/v1/bsv/*path` (bearer_optional)