Concepts

Organizations, spaces and test mode

Everything lives in one space, in one mode. Your key decides which; test never meets live.

Organization · spaces × modes

  • An organization's data lives in spaces: its shared space, where the owner and admins work, and each member's personal space.
  • Every space exists in test and in live: test and live data never mix.
  • An API key belongs to one space, in one mode: it sees and creates that space's data only.
  • An organization key sends On-Behalf-Of with a member's userUuid to act in that member's space, in the key's own mode.
Test and live never mix. A key works in one space, in one mode.

The organization and its people

The tenant is an organization. People belong to it through a role:

RoleWhoWorks in
ownerexactly one per organizationthe shared space, both modes
adminthe owner's teamthe shared space, both modes
usera member: a seller, a creator, a sub-merchanttheir own space, per mode

The owner and the admins are the team. Members join by invitation only (marketplace). qbf.cash's handles are members of QBitFlow's own organization, in live only; an API key never reaches qbf.cash's routes.

Spaces

  • The shared space holds the organization's own products, payments and customers.
  • A member's space holds theirs. They sell from it; the team can act in it.
  • One space per request. Reads list that space's records; writes land in it. Another space's record answers 404.
  • Each exists in test and in live. A record's test says its mode, and userUuid names the member on a member's record. A record never names its organization.

An organization key acts as an admin in the shared space; a member's key acts as that member, in their space. GET /me answers what the key is: its role and its space, mode included (API keys).

A reference (a payment's, a customer's) is unique per space and mode: the same one can exist once in test and once in live. Emails may repeat: several customers can share one, and a payer's typed email only ever reuses the space's one unverified customer with it, never one you created (pass customerUuid or customerReference to name one).

Test mode

The credential picks the mode, never a request: a test key only ever sees and creates test data, on test networks, paid with faucet tokens (faucets).

WhatTestLive
Networkstest networksmainnets
Pricesat most $5 per checkout, fees included (above: 400 validation_failed, the cap in details.max)no cap
Redirect URLshttp:// allowed (a local shop)https:// only
Subscription frequencyat least 5 minutesat least 1 hour
Subscription billsonly when you trigger themon their due date
Customer emailsnever sentsent

Test mode

Nothing carries over to live: a live key, live wallets and currencies, products and customers, webhook endpoints (each with its own secret), settings and policies (live redirect URLs in https://) and, for a marketplace, members invited again. The team itself works in both modes.

On-Behalf-Of

An organization key sends On-Behalf-Of: <userUuid> to act as one of its members, in the key's mode: products, checkouts, customers, payments, subscriptions, settings, webhook endpoints.

Act in a member's space

client-on-behalf-of.sh

# An organization key acting in a member's space: the request reads the member's products, in the# key's mode. Without the header, the same key reads the organization's own.curl https://api.qbitflow.app/v2/products \  -H "X-API-Key: $QBITFLOW_API_KEY" \  -H "On-Behalf-Of: $MEMBER_UUID"# 200: a page of Product ({ items, hasMore, nextCursor? })
// An organization key acting in a member's space: every request of seller sends// On-Behalf-Of. It shares client's configuration.const seller = client.onBehalfOf(memberUuid);const products = await seller.products.list();console.log(`${products.items.length} products on the first page of the seller's space`);
# Every request of this client sends On-Behalf-Of: it acts in the member's space.seller = client.on_behalf_of(member_uuid)  # shares the connections and settings of clientproducts = seller.products.list()print(len(products.items), "products on the first page of the seller's space")
// A client acting in the member's space (organization key): every request sends// On-Behalf-Of. It shares the connections and configuration of client.seller := client.OnBehalfOf(memberUUID)products, err := seller.Products.List(ctx, nil)if err != nil {	log.Fatal(err)}fmt.Println(len(products.Items), "products on the first page of the seller's space")
// An organization key acts in a member's space: every request sends On-Behalf-Of.$seller = $client->onBehalfOf($memberUuid); // shares $client's transport and configurationecho count($seller->products->list()->items), " products on the first page of the member's space\n";
  • The role is user. Admin routes (/members, /invitations, the key list) answer 403: call them without the header.
  • A member of this mode. Someone outside it (the other mode only, removed, the owner, an admin) answers 404; a value that isn't a uuid, 403. A member's own key can only name itself.
  • API keys only. A dashboard session ignores the header and switches space instead.
  • Your team's authority, nothing more. It passes the member policies, sets the settings your team controls for a member and gives a checkout's redirect URLs. It never touches what is the member's alone (their password, account and wallets), writes no policy (no API key does) and bypasses no guard.

An event's envelope names the member's space in userUuid: send it as On-Behalf-Of to read the record.