TBBNMerchantDocs
API

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.

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 sellerId out and an item is listed under your store's name. For a consignor's item, send sellerRef — 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}/availability with {"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 availability or quantity, or drop the row; the change applies on the next pull, or straight away with POST /v1/merchant/feed-source/fetch-now. See the catalog feed guide.

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

  1. Accepted and reserved. When an offer is accepted, both sides' items are reserved (reservation.created) and the session reaches RESERVED — the same as every trade.

  2. Settlement starts. Instead of checkout.started, your side gets trade.external_settlement_started with the amount your side owes the other (0 on an even swap), the currency, and reportBy — 14 days away. Your side's payment row shows settlement: "EXTERNAL" and externalSettlementDueAt.

  3. 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 for failed). Only a merchant on that side can report it, and its first report is final. Report completed only 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_UNRECIPROCATED and their own purchase stands; otherwise the whole trade is cancelled and every reservation released.
  4. The deadline. reportBy is 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_extended with the new reportBy. A second extension, or one after the deadline, is 409. If the deadline passes unreported, your side is released as failed automatically (trade.external_settlement_reported with automatic: true), with the same results as a reported failure.

  5. 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 — or completed after your side was released — is 409 with a message saying what was recorded. You can also send an Idempotency-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 paidThe side that failed
Its purchaseStands. 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.
InventoryIts 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.
MoneyPaid 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 feesThe $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

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 with tbbn_trade_session_id and tbbn_side added.

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:

FieldMeaning
tradeSessionId, sideThe trade, and your side (the items you sell).
buyerSide, buyerSellerIdThe trader checking out with you — always the other side's.
currencyThe trade's currency.
originalPrice, taxBaseYour items' listed prices, for your own tax calculation.
tradeValue, otherSideTradeValueEach side's trade value.
differenceAmountThe difference the buyer owes, or 0.
commissionRate, commissionAmount, commissionOffset, commissionShortfallYour commission, how much of it comes out of your seller's share, and the rest.
checkoutFeeTBBN's checkout fee, when you pass it on; otherwise 0.
sellerNetCashWhat your seller is left with from the difference after commission.
tradeAmountWhat 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 via POST /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.