WalletD developers
Guides

The ecosystem: clients, offerings, discovery

Many businesses in one shared consumer loop: clients, offerings, discovery, what a sale earns, and client-funded rewards.

Everything so far assumed one business integrating a wallet for its own users. The ecosystem is the other shape: many businesses in one shared consumer loop, where a consumer holds a single wallet and spends it at any of them.

If you are building a closed-loop wallet for your own users, you can skip this page.

The pieces

PieceWhat it is
ClientA business in the loop. Owns a merchant, a settlement account, and a marketing budget
OfferingSomething a client sells: an item (one-off) or a plan (recurring)
DiscoveryThe consumer-facing directory of published offerings

Client staff authority is organization membership, not a role you assign. A person's token carries the organizations they belong to, and every client surface checks membership per call. One human can belong to several organizations, so no "switch account" state exists on the server.

Onboard a client

A consumer becomes a business in one call, on their own token:

curl -sS -X POST "$WALLETD_API/v1/clients" \
  -H "Authorization: Bearer $CONSUMER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"display_name": "Bloom Coffee", "category": "restaurants", "description": "Specialty coffee"}'

One idempotent call creates the organization, a merchant with its settlement account, a floor-enforced marketing account, and the catalog profile. Retrying with the same name converges on the same client rather than making a second one.

One gotcha worth planning for: the founder's existing token does not yet carry the new organization. Refresh the token (or prompt for a fresh login) immediately after onboarding, rather than letting them hit a confusing 403 against a business that demonstrably exists.

A new client is created pending. It can do everything except reach an audience: edit its profile, create offerings, price them, edit them, list them. Publishing waits for the platform to approve it, and until then POST .../publish returns 409 client_not_approved and the client is absent from discovery. Show that state in your UI rather than letting a merchant discover it by clicking.

Once you have a client, find it again with:

curl -sS "$WALLETD_API/v1/clients" -H "Authorization: Bearer $FOUNDER_TOKEN"

This resolves from the organizations in the caller's token, so you never have to store the client id yourself.

Publish an offering

# Draft
curl -sS -X POST "$WALLETD_API/v1/clients/$CLIENT_ID/offerings" \
  -H "Authorization: Bearer $FOUNDER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind": "item", "title": "Flat White", "amount": 450}'

# Recurring
curl -sS -X POST "$WALLETD_API/v1/clients/$CLIENT_ID/offerings" \
  -H "Authorization: Bearer $FOUNDER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind": "plan", "title": "Coffee Club", "amount": 1500, "interval": "monthly"}'

# Publish
curl -sS -X POST "$WALLETD_API/v1/clients/$CLIENT_ID/offerings/$OFFERING_ID/publish" \
  -H "Authorization: Bearer $FOUNDER_TOKEN"

Lifecycle is draft to published to archived. Only published offerings of active clients are discoverable, and publishing twice is 409.

Offerings can be edited in place, which matters because archiving is final and an archived offering can never be republished:

curl -sS -X PUT "$WALLETD_API/v1/clients/$CLIENT_ID/offerings/$OFFERING_ID" \
  -H "Authorization: Bearer $FOUNDER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Flat White", "amount": 450, "image_url": "https://example.com/flat-white.jpg"}'

A draft can change anything. A published plan can change how it presents itself (title, description, image, metadata) but not its price, because subscribers are already paying it; repricing one is 400, so archive it and publish a replacement. A published item may be repriced, and every change is recorded in its price history. Items also carry variants, cost-plus pricing and stock; see Products, stock, orders and import.

What a sale earns you

curl -sS "$WALLETD_API/v1/clients/$CLIENT_ID/sales" -H "Authorization: Bearer $FOUNDER_TOKEN"

Returns a summary plus the sales themselves, each with what the buyer paid, the marketplace commission, the payment fee, anything refunded, and the net that reached settlement. The identity holds per row and in the summary: net = amount - fee - commission - refunded. Do not compute a merchant's earnings from the payment amount alone; commission and fee are separate for a reason, and the loop may charge either, both, or neither.

Discovery and purchase

curl -sS "$WALLETD_API/v1/explore/clients?q=coffee" -H "Authorization: Bearer $CONSUMER_TOKEN"
curl -sS "$WALLETD_API/v1/explore/offerings/$OFFERING_ID" -H "Authorization: Bearer $CONSUMER_TOKEN"

Then buying takes only the offering id (for a product with several variants, or more than one unit, use orders instead):

curl -sS -X POST "$WALLETD_API/v1/explore/offerings/$OFFERING_ID/purchase" \
  -H "Authorization: Bearer $CONSUMER_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: buy-$OFFERING_ID-cart-771" \
  -d '{}'
curl -sS -X POST "$WALLETD_API/v1/explore/offerings/$PLAN_ID/subscribe" \
  -H "Authorization: Bearer $CONSUMER_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sub-$PLAN_ID-user-42" \
  -d '{}'

No amount, no merchant, no currency in the request. The catalog is the price authority, so a client cannot be charged a price a consumer's app made up, and a tampered client cannot pay less. If your app sends an amount here, you have found a different endpoint.

Purchasing a plan is 422 not_an_item; subscribing to an item is 422 not_a_plan.

Client-funded rewards

A client can pay for its own promotions out of its own earnings.

# Move earnings into the marketing budget
curl -sS -X POST "$WALLETD_API/v1/clients/$CLIENT_ID/marketing/fund" \
  -H "Authorization: Bearer $FOUNDER_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: fund-$CLIENT_ID-aug" \
  -d '{"amount": 200}'

# A rule that draws on it
curl -sS -X POST "$WALLETD_API/v1/clients/$CLIENT_ID/reward_rules" \
  -H "Authorization: Bearer $FOUNDER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind": "cashback", "params": {"rate_bps": 1000}, "caps": {"per_user_day": 500}}'
curl -sS "$WALLETD_API/v1/clients/$CLIENT_ID/marketing" -H "Authorization: Bearer $FOUNDER_TOKEN"

Funding is guarded against the settlement balance: you cannot budget money you have not earned. And when the marketing balance runs dry, the grant is skipped rather than falling back to platform money. A client's promise never spends someone else's balance, which is the property that makes it safe to let clients write their own rules.

What can go wrong

CodeStatusWhat happened
not_client_member403Not a member of that organization. Often a stale token after onboarding
not_an_item / not_a_plan422Purchase and subscribe were swapped
offering_not_found404Unpublished, archived, or not yours
insufficient_funds422Funding marketing beyond the settlement balance, or a consumer who cannot afford the offering

Next

  • Rewards for how rules evaluate.
  • Payouts to get a client's earnings out.

On this page