# The Trade Counter

Round the back, for marketplaces. Your customer pays you; you send us one signed instruction; we deliver the same signed goods the front door sells and bill your account on a statement. Your customer never touches x402.

A trade account on this store's shelf: any platform that resells to agents — a marketplace, an aggregator, a payments layer that hides x402 from its own users — lists our instruments under its roof, collects its customer's money itself, and orders from us by signed webhook. One door, one JSON body, one signed instruction per sale, delivery in seconds, a certificate the end customer can verify against our public key without trusting either of us.

Trade price is retail plus 20% net of your share, rounded up to the cent, billed per delivery and never for a delivery that did not happen. The sandbox account and its check desk are free and need no conversation.

## Try it now, no account
The sandbox account signs with a published secret: `scvd-trade-sandbox-secret-anyone-may-use` (provider key `scvd-trade-sandbox-key`, dialect `canonical`). Real signatures checked, real goods delivered and marked test, a certificate that verifies at /api/verify, and nothing booked to anyone. The check desk on this account prints the signature we expected, since the secret is public anyway. 50 deliveries a day. Start at the check desk: `POST https://scvd.store/api/trade/sandbox/check` with the headers and body you would send to `https://scvd.store/api/trade/sandbox/{item_id}`.

## Why a marketplace would
- **Your customer gets a receipt that is not your word or ours.** Every item mints an ed25519-signed certificate that verifies against a published key, offline, forever. A marketplace reselling opinions has to be trusted; one reselling checkable artifacts does not.
- **No x402 in your customer's path, and none required of you.** One signed POST. No wallet, no facilitator, no chain, no gas. You already know how to sign a webhook; that is the whole integration.
- **The prices are a rule, not a negotiation.** Trade price is retail plus a published uplift, divided by one minus your share, rounded up to the cent. Printed per item at /api/trade/contract. What you charge above it is your business.
- **Delivery first, always.** The goods are produced before anything is booked. A failed delivery leaves no line on your statement, so you never owe for something your customer did not get.
- **You can be integrated before you have talked to anyone.** A sandbox account with a published secret, a check desk that names which of the four signature checks failed, and a catalog feed with every item's copy, specimen and price at your share. Write to the keeper already working.
- **Everything you would want to audit is already public.** Your account's terms, the shelf you may order from, your receivable, the refusal codes by name, the reference signer, and every correction this store has ever had to make.

## Who buys on account
- **An agent marketplace or tool directory that charges per call.** You already collect from your customer in your own unit and your own checkout; our instrument becomes one listing among yours, and the receipt your customer gets verifies against a key that is neither yours nor ours to fake.
- **An agent framework or runtime with built-in credits or billing.** Your agents spend credits, not USDC. One signed POST from your backend turns a credit into a signed audit, anchor or certificate, and the agent never leaves your billing model.
- **A payments layer that hides x402 from its own users.** Your user wanted a receipt, not a rail. You settle with us on a statement in dollars and hand them the same artifact the front door mints, with the chain fields honestly absent.
- **A platform settling in sats, a card, or a currency we do not take.** The counter never sees a payment, so it never cares what the payment was. List at your rate's equivalent of the trade price; the statement bills dollars; the goods are the same.
- **An API aggregator or gateway reselling third-party endpoints.** Every item at the counter has a machine-readable listing on the catalog feed — copy, specimen, price at your share, what the signature does and does not prove — so onboarding it is a row, not a project.
- **An enterprise or internal platform whose procurement wants an invoice, not a wallet.** A trade account is credit with a ceiling, a statement and a named human who records payouts. That is the shape finance already knows, and the daily cap is the limit they will ask about first.

## How it works
0. **The sandbox, first.** Before any conversation, sign against the sandbox account with the secret printed on this page. Real signatures, real goods, marked test, booked nowhere. POST /api/trade/sandbox/check tells you which of the four checks your signer fails and why, without delivering anything. _Check it yourself: The sandbox's secret, dialect and daily cap are on its account row at /api/trade/contract; its deliveries appear on /api/trade/ledger as test, never billed._
1. **The account.** The keeper opens an account by hand: your platform, your signing dialect, the items you may order, a daily cap, a credit ceiling. You issue us one signing secret (and a provider key if your scheme sends one). We hold them as Worker secrets; nothing of ours is ever asked of you. _Check it yourself: Your account's row — items, share, cap, ceiling, mode — is printed at /api/trade/contract the moment it exists, and its receivable is on /api/trade/ledger._
2. **The sale, on your side.** Your customer buys the item from you at your price, in your currency, on your terms. We are not in that transaction and never see it. _Check it yourself: Nothing to check with us — which is the point. Our trade price per item is public; what you charge above it is yours._
3. **The instruction.** Your backend POSTs one JSON body to /api/trade/{account}/{item_id}, signed HMAC-SHA256 over timestamp, nonce and the exact bytes of the body. We check the signature, the five-minute window, and that the nonce has never been seen — on a store that answers the same from every edge. _Check it yourself: The reference signer is published in this repository (src/lib/trade-auth.ts, signTradeRequest); sign a body with it and compare bytes with your own before any account is live._
4. **The delivery.** The goods are made exactly as the front door makes them — the probe runs, the record is written, the certificate is minted — and come back in one JSON object inside thirty seconds. A delivery that fails books nothing: no statement line, no receivable. _Check it yourself: The certificate says settled_via: trade_account, names your account and the trade price, and binds the sha256 of your signed instruction. It carries no chain fields, because no chain was involved, and it verifies free forever at /api/verify/{cert_id}._
5. **The statement.** Each delivery on a live account adds one line to your statement: item, trade price, your share, our net. Outstanding net is the receivable, published per account. You pay on your cadence; the keeper records each payout by hand and the two sides are reconciled against each other. _Check it yourself: /api/trade/ledger prints every account's delivered count, billed and outstanding figures, with the truncation flag any bounded read here carries. Your own rows, both sides, are yours to read at GET /api/trade/{account}/statement, signed like any order._

## The shelf at the counter
Prices at a 5% partner share; your account's row at https://scvd.store/api/trade/contract prints them at yours. List by machine from https://scvd.store/api/trade/catalog.

| Item | Retail | Trade price | Store nets | Fields |
|---|---|---|---|---|
| Certificate of Patronage | $20 | $25.27 | $24.00 | order_ref, agent_name, purpose, callback_url |
| Context Anchor | $1 | $1.27 | $1.20 | summary, order_ref, agent_name, purpose, callback_url |
| A Bitcoin Anchor | $1 | $1.27 | $1.20 | digest, label, order_ref, agent_name, purpose, callback_url |
| The Calling Card | $0.99 | $1.26 | $1.19 | url, order_ref, agent_name, purpose, callback_url |
| The Shop Window | $3 | $3.79 | $3.60 | url, order_ref, agent_name, purpose, callback_url |
| The Once-Over | $5 | $6.32 | $6.00 | url, order_ref, agent_name, purpose, callback_url |
| The Refresh | $1 | $1.27 | $1.20 | url, order_ref, agent_name, purpose, callback_url |
| The Good Buyer | $0.99 | $1.26 | $1.19 | url, max_usd, no_spend_controls, order_ref, agent_name, purpose, callback_url |
| The Company an Address Keeps | $5 | $6.32 | $6.00 | address, order_ref, agent_name, purpose, callback_url |

## The call
`POST https://scvd.store/api/trade/{account}/{item_id}` with one JSON object. Sign HMAC-SHA256 over `timestamp + "." + nonce + "." + exact_raw_body` with the secret you issued us; send it as `X-Trade-Signature: sha256=<hex>` beside `X-Trade-Timestamp` (unix seconds) and `X-Trade-Nonce` (32 hex, fresh each call). Timestamps outside five minutes and nonces seen before are refused. Send `order_ref` on every call. Your own statement: `GET https://scvd.store/api/trade/{account}/statement`, signed over the empty body. Reference signer: https://github.com/seancrecord/scvd-general-store-repo/blob/main/src/lib/trade-auth.ts

## The worked example, every byte
One sandbox order with fixed inputs, every byte shown, so you can diff your signer against ours line by line. The timestamp is in the past on purpose: send it and the door answers stale_timestamp, which is also worth seeing once.

Door: `POST https://scvd.store/api/trade/sandbox/context_anchor`

Body (83 bytes, sent exactly as shown):

```json
{"summary":"The agent was halfway through a migration.","order_ref":"example-0001"}
```

Signing string (`timestamp.nonce.body`):

```
1756900000.0123456789abcdef0123456789abcdef.{"summary":"The agent was halfway through a migration.","order_ref":"example-0001"}
```

sha256 of the signing string: `3cf9ed9d8b994af5dfc5a8d74877e43a10ec1b38b7ca525286a349da044f4de6`

Headers:

```
X-Trade-Key: scvd-trade-sandbox-key
X-Trade-Timestamp: 1756900000
X-Trade-Nonce: 0123456789abcdef0123456789abcdef
X-Trade-Signature: sha256=b0f2877955d537a132badbd658376032f4b73c6151edddce4edce81696608bab
```

HMAC-SHA256 the signing_string with the secret; hex must equal the X-Trade-Signature value after sha256=. If your sha256 of the signing string differs from signing_string_sha256, your bytes differ before any secret is involved.

### A signer to paste
**Node**

```javascript
import { createHmac, randomBytes } from "node:crypto";

const secret = "scvd-trade-sandbox-secret-anyone-may-use"; // your own, once the account is live
const body = JSON.stringify({ summary: "what to remember", order_ref: "your-order-id" });
const timestamp = String(Math.floor(Date.now() / 1000));
const nonce = randomBytes(16).toString("hex");
const signature = createHmac("sha256", secret)
  .update(`${timestamp}.${nonce}.${body}`)
  .digest("hex");

const response = await fetch("https://scvd.store/api/trade/sandbox/context_anchor", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "X-Trade-Key": "scvd-trade-sandbox-key",
    "X-Trade-Timestamp": timestamp,
    "X-Trade-Nonce": nonce,
    "X-Trade-Signature": `sha256=${signature}`,
  },
  body, // the same string you signed, byte for byte
});
console.log(response.status, await response.json());
```

**Python**

```python
import hashlib, hmac, json, secrets, time, urllib.request

secret = b"scvd-trade-sandbox-secret-anyone-may-use"  # your own, once the account is live
body = json.dumps({"summary": "what to remember", "order_ref": "your-order-id"}, separators=(",", ":"))
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
signature = hmac.new(secret, f"{timestamp}.{nonce}.{body}".encode(), hashlib.sha256).hexdigest()

request = urllib.request.Request(
    "https://scvd.store/api/trade/sandbox/context_anchor",
    data=body.encode(),  # the same bytes you signed
    method="POST",
    headers={
        "Content-Type": "application/json",
        "X-Trade-Key": "scvd-trade-sandbox-key",
        "X-Trade-Timestamp": timestamp,
        "X-Trade-Nonce": nonce,
        "X-Trade-Signature": f"sha256={signature}",
    },
)
with urllib.request.urlopen(request) as response:
    print(response.status, response.read().decode())
```

**Go**

```go
package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"net/http"
	"strconv"
	"time"
)

func main() {
	secret := []byte("scvd-trade-sandbox-secret-anyone-may-use") // your own, once the account is live
	body := `{"summary":"what to remember","order_ref":"your-order-id"}`
	timestamp := strconv.FormatInt(time.Now().Unix(), 10)
	raw := make([]byte, 16)
	rand.Read(raw)
	nonce := hex.EncodeToString(raw)
	mac := hmac.New(sha256.New, secret)
	mac.Write([]byte(timestamp + "." + nonce + "." + body))
	signature := hex.EncodeToString(mac.Sum(nil))

	req, _ := http.NewRequest("POST", "https://scvd.store/api/trade/sandbox/context_anchor", bytes.NewBufferString(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Trade-Key", "scvd-trade-sandbox-key")
	req.Header.Set("X-Trade-Timestamp", timestamp)
	req.Header.Set("X-Trade-Nonce", nonce)
	req.Header.Set("X-Trade-Signature", "sha256="+signature)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	fmt.Println(res.Status)
}
```

From a shell: `npx scvd trade check context_anchor` signs a sandbox order with the published secret and asks the check desk; `npx scvd trade order` delivers. Add `callback_url` to any order and the signed delivery receipt is POSTed there once, after the response.

### Every refusal, by name
- `404 unknown_account` — No trade account by that name. Check the path segment against your account row at /api/trade/contract.
- `503 counter_closed` — The replay store is unreachable on this side, so the counter fails closed rather than risk honouring one instruction twice. Do not retry in a loop; the condition is ours to fix and the keeper is paged. Retry after a minute.
- `503 account_not_provisioned` — This account's signing secret is not set on this side yet, so nothing can be verified and nothing is delivered or billed. Opening the account is the keeper's hand once a secret exists between us. Until then the sandbox is open, and this account's check desk still answers every check it can without a secret: headers, timestamp, nonce shape, signature shape, and the sha256 of your signing string.
- `413 body_too_large` — The body is over one mebibyte. Send only the item's fields; the terms document lists them.
- `401 missing_headers` — A required signing header is absent. Send every header your dialect names, on every call.
- `401 bad_provider_key` — The provider key does not match the one on the account. Check which key you issued us; a rotated key needs the keeper's hand on this side.
- `401 bad_timestamp` — The timestamp is not an integer in the unit your dialect states. Unix seconds or milliseconds as your account row says, digits only.
- `401 stale_timestamp` — The timestamp is outside the window, past or future. Sign at send time with a synchronised clock; do not reuse a signed request.
- `401 bad_nonce` — The nonce is not the shape your dialect requires. Thirty-two hex characters, fresh per request.
- `401 bad_signature` — The HMAC does not verify against the secret in service or the previous one. Sign the exact bytes you send, in the order timestamp.nonce.body, and compare with the reference signer before assuming the secret is wrong.
- `409 replayed` — This nonce (or this exact instruction) has already been presented. Nothing was delivered on this call. If you are retrying a timed-out order, send order_ref and a FRESH nonce: the same order_ref returns the original delivery.
- `404 not_found` — No delivery on this account carries that order_ref. Check the reference; deliveries are searchable by order_ref for as long as the rows are kept, and a delivery without one cannot be recovered this way.
- `404 not_at_the_counter` — The item is not on your account, or not traded at the counter at all. Order from the items on your account row; ask the keeper to add one.
- `400 bad_request` — The body is not a JSON object, or the item's required field is missing or malformed. The terms document names each item's fields; the error names the one at fault.
- `400 target_refused` — The URL is not one we will probe: private, loopback, non-https, or our own hostname. Send a public https door. We do not sell audits of ourselves.
- `429 desk_rate_limited` — The check desk has answered this account's hourly budget of diagnoses. Wait for the hour to roll. The budget bounds how fast anyone can use the desk as an oracle; a partner debugging a signer needs a handful of calls, not hundreds.
- `403 cross_site_refused` — A browser form was submitted from another origin. The payout form is only accepted from the store's own admin page; a script records payouts with a JSON body instead.
- `429 credit_ceiling_reached` — Your live account's unpaid net has reached its credit ceiling. Settle the statement; the ceiling is printed on your account row and the outstanding figure on /api/trade/ledger. The keeper can raise it.
- `429 cap_reached` — Your account has ordered its daily cap. The cap is the blast-radius bound on a leaked secret, not a rate limit; it resets at UTC midnight and the keeper can raise it.

## Questions people ask
**What does the certificate say about payment?**

That the sale settled on a trade account, which account, at what trade price, and the digest of the instruction you signed. It does not say USDC, Base, Solana or Polygon, because none of them were involved, and it does not name a payer wallet, because there was none. A receipt that claimed otherwise would be the kind of false claim this store files against other people.

**Do you verify that my customer actually paid me?**

No, and we say so on the artifact. What we verify is that YOU instructed the delivery. What we check afterwards is your statement against your payouts. A marketplace's word about its own customers is the one thing this door has to take on trust, and the daily cap, the receivable ceiling and the test mode are the shape of that trust.

**Why is the trade price above the front door's?**

Because an account is credit. The uplift covers the receivable, the reconciliation and your share, by a formula printed beside every price. If your customers would rather pay less and sign an x402 authorization, the front door is open and always will be.

**My call timed out. Did you deliver?**

Possibly. Send order_ref on every call: a retry carrying the same order_ref within a day returns the original delivery, unbilled a second time. Without an order_ref, a retry with a fresh nonce is a fresh sale.

**Can I rotate my secret without downtime?**

Yes, either way your side works. If you can run two secrets at once, tell the keeper the new one; for the handover window both verify and the response says which one signed, then the old one is unset. If your side cuts over — replace, pause, resume — the keeper sets the new value while you are paused and you resume against it.

**I take payment in sats, cards, credits or a currency you do not accept. Does that work?**

Yes; it is the point. This door sees no payment, so it does not care what yours was. Every price and statement line here is in US dollars; you list at your own rate's equivalent of the trade price and bill your customer however you bill. We print no figure in any other unit, because we hold no exchange rate and will not pretend to.

**I already sign webhooks a different way. Do I have to change?**

No. The differences between marketplaces — header names, the order of the signed string, seconds or milliseconds, a signature prefix, whether a provider key travels alongside — are a dialect row on your account, not a branch in the code. Two dialects are published on the contract today; yours is a row the keeper adds when the account opens.

**Does my share improve with volume?**

The standard offer does: the share rises with live deliveries in the calendar month, by a ladder printed on the contract, and the trade price is derived from the share so the store nets the same at every step. An account with its own contract keeps its own terms.

**My signature is rejected and I cannot see why.**

POST the same headers and body to /api/trade/{account}/check. It runs the four checks and reports each — headers present, provider key, clock skew in seconds, nonce shape, and whether the HMAC verified under the secret in service, the previous one, or neither — and prints the sha256 of the signing string we computed so you can compare it with yours. It delivers nothing and consumes no nonce. On the sandbox it also prints the signature we expected.

**Who handles refunds for my customer?**

You do. You collected the payment; this store took none and cannot return any. A refund on your side does not reverse a statement line here — the goods were delivered — unless the keeper agrees one by hand.

**How do I open an account?**

Prove your signer on the sandbox first, then write to the store — POST /api/letter — with your platform, the dialect you sign in (or that you will use ours), the items you want, and expected daily volume. A human reads it. Accounts open in test mode, so you can run real calls against real goods before anyone owes anyone anything.

## What this is not
Not a payment rail: no money moves through this door, and the store does not verify that any moved through yours. Not a discount channel: trade prices sit above the front door's, by a published rule, because an account is credit. Not an escrow, a guarantor, or a dispute court between you and your customer. And not a way to buy the keeper's hands: the human-queue shelf is not at this counter.

**Honest limits.** The store sees no payment on this door and signs none. A trade certificate proves the store delivered on a signed instruction from a named account; it does not prove the account's customer paid, was refunded, or existed. The receivable is derived from delivery rows on a capped read that says when it was cut short; the daily cap is counted on eventually consistent storage and can overshoot by a unit under a race. Nothing here is a rail, and the payout side of a statement is recorded by a person.

The contract: https://scvd.store/api/trade/contract · the ledger: https://scvd.store/api/trade/ledger · this room as JSON: https://scvd.store/trade.json · corrections: https://scvd.store/corrections
