Trade Engine integration walkthrough
This is the story to read once, end to end, before touching the API reference. It's the mental model; endpoint-level detail lives there.
1. Create a Business, then get approved
Every Merchant links to a Business profile first: register an account, create a registered
Business at business.tbbnetwork.com and verify it by completing Stripe Connect. Then apply from
the Business's Linked merchants page — or call POST /v1/merchants with its businessId,
signed in as the Business owner or an Admin. Step by step: Apply for a Merchant account.
Your business rep gets a temporary account link by email. It lasts 7 days and lets you upload
documents, track review status, and resubmit if something's missing. Once approved, that
temporary account becomes your permanent Merchant Dashboard login — same email, same
one-time-code login, now persistent. ("Merchant Dashboard" here is the app for a specific
integration; the Business profile itself — which can run more than one Merchant — is managed
separately.) Invite developers and account managers from the dashboard; they get scoped roles
(DEVELOPER for API/integration work, ACCOUNT_MANAGER for seller/billing management) — see
Team roles.
2. Get your keys
From the dashboard, generate a sandbox API key first — free, unlimited (rate-limited) usage, no
production data. Get one instantly with no application at all if you
just want to explore the API before applying. Build your integration against sandbox, then
generate a production key (tbbn_sk_live_...) once you're approved — that's what starts counting
against your plan's included usage. See Pricing.
3. Link your sellers — or trade for them directly
Every listing names a seller, who must be one of your own. The Seller account linking guide has the full flow, parameters, responses and webhook. In short:
Option A — link their TBBN account. Register an OAuth client (merchants.tbbnetwork.com →
Account linking, or POST /v1/oauth-clients), redirect the seller's browser to
account.tbbnetwork.com/link/authorize?client_id=...&redirect_uri=...&scope=link:seller&state=...&merchant_seller_ref=...&code_challenge=...&code_challenge_method=S256,
and once they approve, your server exchanges the code at POST /v1/link/oauth/token for their
sellerId. If you've verified a new seller's email and phone yourself,
POST /merchant/sellers/verify creates and links their profile; if an account already uses those
details it returns requiresConfirmation: true and the seller links through the OAuth flow.
Option B — no account needed. If your plan tier allows it (Growth/Enterprise), call
POST /merchant/sellers/headless with {merchantId, merchantSellerRef, name} to create a
"headless" Seller — a trade participant tracked entirely by you, with no linked TBBN account at
all. This can't accumulate a cross-merchant reputation history the way a linked account can, since
there's no single identity behind it across merchants.
4. Sync your listings
POST/PUT/DELETE /merchant/listings — you own listing content, TBBN just mirrors it. To check
whether an item is already on TBBN, look it up by your own reference:
GET /merchant/listings?ref=<your id> returns exactly that listing (or none), whatever its status unless deleted. Set
visibility per your plan tier: PRIVATE (internal only), GLOBAL (TBBN Marketplace + other
global merchants), BOTH, or ENTERPRISE (only your hand-picked enterprise trading merchants —
Enterprise tier only). Set listingType (SALE/TRADE/BOTH) — a SALE listing is discovery
only, never offer-able; TBBN redirects visitors to merchantItemUrl (set it, or visitors see a
plain "not available for TBBN checkout" message) instead of orchestrating a trade for it. Include
wants if you want your sellers' listings to show up in match recommendations, not just search.
Without them, your listings are matched on what your catalog offers (see
Listing kinds).
A listing's seller is optional. Leave it out and the item is your store's own, shown under your
store's name — enough for listing your catalogue for reach. For a customer's item send
sellerRef, your own reference for that customer (any stable value; it needn't be your customer
id): it's shown under your store until they link their TBBN account with the same reference, then
it's theirs automatically. Once a customer is linked you can send their TBBN sellerId instead,
which must be one of your own sellers from section 3 (any other is 403 SELLER_NOT_LINKED). An
update that names no seller keeps the listing's seller. A listing reserved in a trade keeps its
seller until the trade ends. See
Listing before your sellers link.
If you'd rather not write API-calling code, see
the catalog feed guide: register a feed file with
POST /v1/merchant/feed-source and TBBN pulls it every 24 hours, or on demand with
POST /v1/merchant/feed-source/fetch-now. A feed can name each row's seller
(external_seller_id). For one-of-a-kind items or stock that also sells in store, direct API sync
is the better fit: POST /merchant/listings/{id}/availability takes an item down the moment it
sells, where a feed waits for its next pull.
5. Handle offers and trade sessions
Offers and trade sessions happen on TBBN's side (or your embedded widget, or TBBN Marketplace) —
you don't build negotiation UI unless you want full API control instead of the widget. You'll
receive webhooks (offer.created, offer.accepted, trade.accepted) as things progress. See
Webhooks & events.
The trigger button that starts this flow on your storefront doesn't have to say "TBBN" — name it
whatever fits your own brand and audience. <tbbn-trade-button label="Swap this item"> (or
"Trade," "Barter," "Barter this item," or anything else you choose) renders with your own copy;
the same heading/submit-label attributes on <tbbn-offer-modal> rename the offer dialog that
opens from it. Your Merchant Dashboard settings page saves a default label and shows you the
exact embed snippet to copy, so you only have to choose it once.
Listing for reach only
You can list with TBBN purely for wider reach, without using the trade engine: set
matchingEnabled: false on your Merchant (or list items as SALE). Those listings appear in
search and on TBBN Marketplace, but never receive offers; a visitor who wants one is sent to its
merchantItemUrl and buys on your own site.
- No sellers needed. Leave
sellerIdout and an item is listed under your store's name. For a consignor's item, sendsellerRef— your own private reference for them — and they can link a TBBN account later to have those items credited to them; nothing requires it. See Listing before your sellers link. - No sales reporting. You don't report those sales to TBBN — not in real time, not at all.
- Keep each listing's status current, through whichever way you list:
- API — when an item sells out or comes back, call
POST /merchant/listings/{id}/availabilitywith{"status": "SOLD"},"UNAVAILABLE"or"ACTIVE", or update or delete the listing. Status changes, updates and listing syncs are all free (see Pricing). - Catalog feed — set
availabilityorquantity, or drop the row; the change applies on the next pull, or straight away withPOST /v1/merchant/feed-source/fetch-now. See the catalog feed guide.
- API — when an item sells out or comes back, call
Everything from here on applies only when you use the trade engine.
Choose your integration: matching only, or matching plus checkout
TBBN never takes payment for a trade — the money always moves on your checkout. What you choose is how much of the rest TBBN coordinates for your side of a trade:
- Checkout orchestration (the default). TBBN works out who owes whom and what your commission comes to, shows both traders the quote before they accept, sends the trader receiving your item to your checkout with the amount, tracks your payment and fulfillment callbacks, and completes the trade. You don't build any barter logic, but you do run one page that takes the trader and reports the result — section 6.
- Matching only. TBBN matches, negotiates and reserves the items, then your platform runs the exchange end to end — payment, delivery, everything — and reports the outcome to TBBN once. Use this when your own checkout and fulfillment already handle barter trades.
Configuration
One field on your Merchant, checkoutEnabled: true (default) for checkout orchestration,
false for matching only. Set it in your Merchant Dashboard settings ("Checkout
orchestration"), or with PATCH /v1/merchants/{id} and {"checkoutEnabled": false} — a signed-in
representative with the OWNER or ADMIN role, plus a step-up code once your merchant is approved
(an API key can't change your merchant's own settings). It applies to trades whose checkout opens
after the change. (matchingEnabled: false takes your listings out of
matching and offers entirely — see "Listing for reach only" above.)
Each side runs its own way
A trade has two sides, and each follows its own merchant's setting. A matching-only merchant can trade with a merchant using orchestration, or with an individual seller (who always uses TBBN checkout): the orchestration side gets checkout exactly as usual, and the matching-only side settles on its own platform. One merchant's choice never changes the other side's experience.
The matching-only lifecycle
-
Accepted and reserved. When an offer is accepted, both sides' items are reserved (
reservation.created) and the session reachesRESERVED— the same as every trade. -
Settlement starts. Instead of
checkout.started, your side getstrade.external_settlement_startedwith theamountyour side owes the other (0 on an even swap), thecurrency, andreportBy— 14 days away. Your side's payment row showssettlement: "EXTERNAL"andexternalSettlementDueAt. -
You settle and report once. Run payment and delivery on your platform, then call:
POST /v1/trade-sessions/{id}/external-settlement { "side": "B", "outcome": "completed" }or
{ "side": "B", "outcome": "failed", "reason": "..." }(a reason is required forfailed). Only a merchant on that side can report it, and its first report is final. Reportcompletedonly once the item has actually been delivered — never early to beat the deadline; extend it instead (step 4).- completed — your side counts as paid and fulfilled. If the other side is done too, the
trade completes (
trade.completed). If the other side uses TBBN checkout and hasn't paid yet, its counterparty hold starts, exactly as if you had paid through TBBN checkout. - failed — your side's reservation is released. If the other side has already paid, the
trade closes as
CLOSED_UNRECIPROCATEDand their own purchase stands; otherwise the whole trade is cancelled and every reservation released.
- completed — your side counts as paid and fulfilled. If the other side is done too, the
trade completes (
-
The deadline.
reportByis 14 days after checkout opened. If delivery genuinely needs longer, extend it once, by 1 to 14 days, with a reason, before it passes:POST /v1/trade-sessions/{id}/external-settlement/extend { "side": "B", "days": 7, "reason": "Courier delay" }Both sides get
trade.external_settlement_extendedwith the newreportBy. A second extension, or one after the deadline, is409. If the deadline passes unreported, your side is released as failed automatically (trade.external_settlement_reportedwithautomatic: true), with the same results as a reported failure. -
Retries and timeouts. The report is safe to repeat: sending the outcome already recorded returns the trade unchanged, so after a timeout just send it again (or read your side's payment row with
GET /v1/trade-sessions/{id}). A different outcome from the one recorded — orcompletedafter your side was released — is409with a message saying what was recorded. You can also send anIdempotency-Key(see API conventions).
The session's own status is EXTERNAL_SETTLEMENT only when both sides are matching-only; a mixed
trade moves through WAITING_CHECKOUT_A/B as usual, with one side EXTERNAL. A trade with a
matching-only side isn't subject to the 7-day pre-payment expiry — its 14-day report deadline
applies instead. The payment and fulfillment callbacks in sections 6–7 are refused for an
EXTERNAL side; report it with the call above instead.
When one side fails and the other's purchase stands
If one side has paid (or reported completed) and the other side fails — reported, released at
its deadline, or closed after its counterparty hold — the trade closes as CLOSED_UNRECIPROCATED:
| The side that paid | The side that failed | |
|---|---|---|
| Its purchase | Stands. Its merchant fulfills it as normal; once fulfillment has started, no refund is due because the other side didn't complete, as long as the item arrives as agreed. | Never completed — nothing to fulfill. |
| Inventory | Its item stays reserved for its own order; its merchant marks it sold as it normally would. If fulfillment hasn't started once the counterparty hold has run, its merchant may excuse it instead (section 11). | Its reservation is released and the listing is live again. |
| Money | Paid on its own merchant's checkout — TBBN never holds it. Returns and refunds follow that merchant's own policy. | Nothing was paid through TBBN. Anything its own platform collected is refunded, or not, under that merchant's policy. |
| TBBN fees | The $1.50 checkout-orchestration fee for its completed checkout (TBBN-checkout sides only). | None. |
Billing
The trade negotiation is metered as for any trade (see Pricing). There is no checkout-orchestration fee for a matching-only side — TBBN runs no checkout for it. An orchestration side pays its $1.50 per completed checkout as usual.
6. Handle checkout — the page you run with checkout orchestration
Trade setup: your commission and checkout link
Before your items can be offered or traded, set two things in your Merchant Dashboard
(Settings → Trade setup), or with PATCH /v1/merchants/{id}:
commissionRate— your commission on each of your items traded, as a percent of the item's listed price (0–100, up to two decimals; 0 if you take none). Every change is kept (GET /v1/merchants/{id}/commission-rates), and a trade always keeps the rate it was accepted with.checkoutEndpointUrl— your trade checkout link (https). TBBN sends the trader here withtbbn_trade_session_idandtbbn_sideadded.
Optionally: passCheckoutFee (the trader pays TBBN's $1.50 checkout fee instead of you),
passProcessingFees (you add your card processing fee at checkout) and shipFromRegion (the
state or region you ship from, shown to traders).
The trade quote
Before either trader accepts, both see the same quote: each trader checks out with the store of the item they receive, and the quote shows what each pays there. The side whose trade value is lower (the payer) pays the difference. Your commission comes first out of any difference your own seller is due; whatever is left is charged to the trader at your checkout:
commission = your rate × the item's listed price
taken from seller = min(commission, the difference your seller is due)
charged to trader = commission − taken from seller
your checkout total = difference the trader owes (if they're the payer)
+ commission charged to trader
+ TBBN's checkout fee (if you pass it on)
+ any tax, card fees and shipping — added by you
Sending an offer and accepting one each confirm the quote by its fingerprint
(POST /v1/offers/quote, GET /v1/offers/{id}/quote); accepting locks it, so a later change to a
price or to your rate never changes a trade already agreed. If you act for your sellers through the
API, show them the quote and pass its quote.fingerprint when you create or accept an offer.
Taking the trader through checkout
When a trade session reaches RESERVED, you receive a checkout.started webhook for your side —
the side whose items you sell — and the trader receiving them is sent to your checkout link. When
both sides' items are yours, you get both checkouts, one per trader. A checkout with nothing to
collect has an amount of 0 and uses the same step to confirm the exchange (section 8). The webhook
carries what your checkout needs, from the locked quote:
| Field | Meaning |
|---|---|
tradeSessionId, side | The trade, and your side (the items you sell). |
buyerSide, buyerSellerId | The trader checking out with you — always the other side's. |
currency | The trade's currency. |
originalPrice, taxBase | Your items' listed prices, for your own tax calculation. |
tradeValue, otherSideTradeValue | Each side's trade value. |
differenceAmount | The difference the buyer owes, or 0. |
commissionRate, commissionAmount, commissionOffset, commissionShortfall | Your commission, how much of it comes out of your seller's share, and the rest. |
checkoutFee | TBBN's checkout fee, when you pass it on; otherwise 0. |
sellerNetCash | What your seller is left with from the difference after commission. |
tradeAmount | What the buyer pays you before tax, card fees and shipping. |
Add tax, any card processing fee and shipping, exactly as for any other sale. Every trade's figures
are also in your trade records — see
Tax and accounting for merchants. After payment, call back
with payment.completed or payment.failed for your side. Do not wait for the other side to
pay first — both checkouts are live simultaneously the moment the session is RESERVED.
Traders see and explicitly agree to the applicable hold terms (below) before this step — nothing about that consent step needs building on your end, it happens on TBBN's own trade page.
7. Fulfill
Your side's fulfillment starts the moment your checkout reports payment.completed — the
trader who paid you gets what they paid for, the same independence as checkout above. There's no
joint "both sides paid" gate: report your fulfillment status with your side as soon as you're
ready, independent of the other checkout. Fulfillment, shipping, tracking, returns — all yours, same as any other order. The
trade session auto-completes once both sides have separately reported fulfillment.completed
— that's a bookkeeping milestone, not something either merchant needs to coordinate.
8. Equal-value trades — no cash difference
An even swap runs through exactly the same checkout flow as any other trade. Both sides still get
a payment record — with no difference to pay, each checkout's amount is just your commission (and
a passed-on checkout fee), or 0 at a 0% rate. Your checkout still receives the trader, and still
reports back payment.completed the same way — at 0 you're confirming the exchange, not
processing a $0 charge. Two
things don't disappear at $0: the $1.50 per-side checkout-orchestration fee still applies (a
$0 swap between two of your own sellers still costs $1.50–$3 total — see
Pricing), and tax on the item's value, your own service fee,
and any other charge you set are still yours to collect, same as section 6.
9. Two sellers under the same merchant trading with each other
Available on Growth and Enterprise plans, checked when an offer is accepted. The checkout-orchestration fee is the normal $3.00 total, not the 4× cross-merchant rate, whenever the same merchant is on both sides.
10. Locking your own inventory (Trade Engine listings only)
TBBN's own reservation only locks the item inside TBBN — it does nothing to your storefront or POS
on its own. For any TRADE-type listing (the real barter engine, as opposed to SALE-type
listings, which are discovery-only and were never reserved through TBBN), subscribe your webhook
endpoint to reservation.created, reservation.expired, and reservation.failed, and treat them
as a load-bearing part of your integration: place a real-time hold on your own inventory the
moment reservation.created arrives, and release it on reservation.expired/reservation.failed.
Your hold should follow TBBN's own holding rules — see Trade session states
for the full policy and the effective-hold-window figure this checkout page shows the trader.
11. If the other side never pays
Once a trader has paid at your checkout, your fulfillment proceeds independently (section 7) — it does not wait on, and is not undone by, what happens at the other store. If the trader who should pay at your checkout never does:
- Your items stay reserved for the counterparty hold (7–14 days, see
Trade session states). After that, you can excuse your
checkout via
POST /v1/trade-sessions/:id/excuse-fulfillment: your items are released back to your seller, and the trade closes. Either trader can also close it then viaPOST /v1/trade-sessions/:id/close-unreciprocated. Neither touches the other checkout — that trader paid, and gets what they paid for. - No refund is due because the other side didn't complete. A paid checkout is a sale that stands as long as the item arrives as agreed. TBBN holds no escrow on a Merchant-checkout trade; any other return or refund follows your own policy, exactly as for any other order.
See Trade session states for the exact state machine and Webhooks & events for every payload shape.
