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.
The organization and its people
The tenant is an organization. People belong to it through a role:
| Role | Who | Works in |
|---|---|---|
owner | exactly one per organization | the shared space, both modes |
admin | the owner's team | the shared space, both modes |
user | a member: a seller, a creator, a sub-merchant | their 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
testsays its mode, anduserUuidnames 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).
| What | Test | Live |
|---|---|---|
| Networks | test networks | mainnets |
| Prices | at most $5 per checkout, fees included (above: 400 validation_failed, the cap in details.max) | no cap |
| Redirect URLs | http:// allowed (a local shop) | https:// only |
| Subscription frequency | at least 5 minutes | at least 1 hour |
| Subscription bills | only when you trigger them | on their due date |
| Customer emails | never sent | sent |
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.
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")marketplace/main.go
// 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) answer403: 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.