Changelog
What's shipped.
2026-10-26
Each charge once; the spend reward
- Every usage charge is recorded once against the action it’s for. A retried request or a repeated event report never charges again, and a missed charge is recorded within the hour, from the action itself.
- Webhook deliveries are charged when they get through, never for a failed attempt.
- The spend reward: once TBBN has launched in your Business’s country, spending the amount on the pricing page on usage earns usage credit, once. See the Pricing reference.
2026-10-24
A more accurate API reference
- GET /v1/sellers is documented: the sellers linked to your Merchant, newest link first.
- POST /v1/auth/merchant/login is removed; it only ever answered 501. Sign in with POST /v1/auth/merchant/otp/request and /verify.
- Operations only a signed-in member can use are marked session-only: changing or deleting a member’s own listing, cancelling a recurring Space booking, managing Space API keys, and step-up verification. A Space’s prices, booking consent, a Business’s review summary and a listing’s wants need no credential.
2026-10-22
Where a listing is
- Public listing reads (GET /v1/directory/listings, /v1/search/listings, swap candidates) carry locationCountry: the country the listing is in (ISO 3166-1). Show its name with the place to a viewer in another country — a currency sign like $ can mean a different currency there.
- GET /v1/directory/listings uses the country you pass before a signed-in member’s own, so a page can show the country its visitor is in.
2026-10-12
Link codes; customer linking page
- POST /merchant/sellers/link-code redeems a one-time code a customer made in their TBBN account, linking them under your reference (merchant_seller_ref) like the OAuth handshake. The seller.linked webhook carries linkMethod LINK_CODE. In every SDK as redeemLinkCode.
- Settings → Customer linking (linkAccountUrl on PATCH /v1/merchants/{id}): the page on your site where customers connect TBBN, listed for members under Link a store.
- Listing kinds: kind (GOODS, VEHICLE, PROPERTY, SERVICE, TICKET) and details on listing writes and public reads — see the Listing kinds guide. GET /v1/directory/listings filters by kind, make, minYear, maxYear, offering and minBedrooms.
- preferredSpaceId on listings (a suggested TBBN Space for the meetup or drop-off) and meetupSpaceId / meetupFromListing on offers; an accepted offer starts its trade with that Space agreed.
- GET /v1/directory/storefronts/{slug}: a member’s public storefront. Every SDK has it as directory.storefront.
- Wants have a kind and details, like listings: swap a car for a car, a service for an item, an item for a car. Numbers are ranges (yearMin, mileageMax with mileageUnit, bedroomsMin). A listing of yours without wants is matched on what your catalog offers.
- Members’ listings are local pickup (deliveryMethod is ignored) and need a location; they carry approximate latitude and longitude.
- Catalog feeds take a kind column and detail_<field> columns (detail_make, detail_year, …) for vehicles, homes, services and tickets.
- GET /v1/platform/markets: the countries with their own site on tbbnetwork.com (tbbnetwork.com/us is the default).
2026-10-11
Wants say more; members’ listings need a photo
- A want takes note (what exactly), minCondition (the worst condition accepted — New, Used - Like New, Used - Good, Used - Fair or For Parts) and imageUrl. Matching ignores case in category, subcategory and brand, and ranks an item worse than minCondition as a weak match.
- POST and PUT /v1/listings/individual need at least one photo (422 PHOTO_REQUIRED) and, unless listingType is SALE, at least one want (422 SWAP_WANTS_REQUIRED). They answer publicNow and notPublicReason.
- GET /v1/directory/listings/{id} answers the member who listed an item even before it is public, with viewerIsOwner and notPublicReason.
- POST /v1/matching/candidates returns only listings open to swaps (TRADE or BOTH) from other sellers, with a photo, the cash terms, who it’s from and where; a SALE listing has none. GET /v1/directory/listings takes matchFor (one of your own swap listings) and returns each listing’s compatibilityScore and compatibilityBreakdown, best first.
- GET /v1/localization/locate: where the caller’s connection seems to be — country, and region and city when known. In every SDK as locate().
2026-10-10
Listing syncs are free; marketplace filters and pages
- Listing syncs, uploads and imports are free on every plan — by POST or PUT on /merchant/listings, a catalog feed or a bulk import. Store visits are what a catalog pays for. Your usage still shows the count.
- GET /v1/directory/listings takes buy=1 (listings that can be bought), minPrice and maxPrice, seller=store or seller=person, and page (from 1) instead of a cursor; the response adds page and pageCount. A signed-in member sees their own country’s listings.
- Public listings carry locationLabel ("City, ST" for a person’s own item) and sellerIdentityVerified. Individual listings take city and region.
2026-10-08
Listing syncs count changes, not calls
- A listing sync is counted when a listing is created or changed, by POST /merchant/listings, PUT /merchant/listings/{id} or a catalog feed row alike. Sending a listing again exactly as it is no longer counts — nothing is written or billed — so a daily feed of an unchanged catalog costs nothing. PUT edits that change a listing now count, as a re-send with changes always did. Availability changes and deletes stay free.
- POST and PUT on /merchant/listings answer with the listing, including its id, in test mode and before your country launches (they used to answer 404 although the listing was saved).
- A member’s MEETUP booking needs scheduledAt (400 MEETUP_TIME_REQUIRED); it stays optional with a Space API key.
- POST /v1/businesses/{businessId}/branches takes latitude and longitude, the exact spot when you have it.
2026-10-07
List without sellers; items follow sellers when they link
- A merchant listing’s sellerId is optional. Leave it out for your store’s own item (shown under your store’s name), or send sellerRef — your own reference for the customer, never necessarily your customer id — and the item is shown under your store until that customer links their TBBN account with the same reference, then becomes theirs. An update that names no seller keeps the listing’s seller.
- Linking: merchant_seller_ref is optional on the authorize link. The token exchange returns the customer’s TBBN sellerId to store and send on their listings, and attributedListings — how many of your listings moved to them. A reference already linked to a different account is 409 SELLER_REF_LINKED_TO_ANOTHER. A customer who links under a headless seller’s reference takes over its link and listings.
- Catalog feeds: defaultSellerId is optional, and external_seller_id takes your reference or a TBBN seller id. A row whose seller hasn’t linked yet is listed under your store instead of being skipped. References beginning tbbn: are reserved.
2026-10-06
Test mode, drop-off recipients with accounts
- Breaking: test mode. Test keys (sk_sandbox_) now come from your Merchant Dashboard in Test mode and work only on your Merchant’s test account — its own sellers, listings, offers, trades and webhooks, never public, never trading with live listings, not billed. The anonymous POST /v1/sandbox/merchants is gone; a test key made before today returns 401 SANDBOX_KEY_RETIRED. POST /v1/api-keys makes test keys only in test mode and live keys only in live mode (409 TEST_MODE_REQUIRED / LIVE_MODE_REQUIRED). See Test mode.
- API key scopes are API areas (listings, offers, trade-sessions…); an empty list is the whole API. Keys the dashboard created with listings:write / sellers:write now reach listings and sellers as intended.
- Breaking: Space API test mode. A test key (sk_space_sandbox_) now makes test bookings (livemode: false) — never charged, never holding the Space, nobody notified — whose links open a test page where consent and payment are simulated (GET /v1/space/test-bookings/{id}, POST …/simulate). A test key lists and cancels only test bookings, a live key only live ones. Before today a test key made real bookings.
- Space drop-offs: the person picking up must have a TBBN account (422 RECIPIENT_ACCOUNT_REQUIRED) and accept before the item can be dropped off (verify-dropoff-pin returns 409 RECIPIENT_NOT_ACCEPTED until then). New: GET /v1/space/pickups, POST /v1/space/bookings/{id}/recipient/respond, PUT /v1/space/bookings/{id}/recipient. The handover no longer accepts the recipient’s emailed link.
2026-10-05
Store logos, seller names, photo limits, handover photos and the Space API for your own Spaces
- Merchants can set a logo (logoUrl on PATCH /v1/merchants/{id}); it identifies the store on every listing it sends, next to the seller’s name. Search and directory results now include sellerName, merchantDisplayName and merchantLogoUrl.
- Listings carry at most 5 photos. A feed row with more keeps the first 5; ingested images are resized to fit 1600px and stored as WebP.
- POST /v1/trade-sessions/{id}/space-proposals/{proposalId}/decline lets the other trader turn a meeting place down.
- GET /v1/billing/invoices/{businessId} lists a Business’s Stripe invoices with their status, amounts and links, and the Billing page shows them.
- New Seller account linking guide: every parameter, response, error and the seller.linked webhook. The token exchange response and seller.linked now include merchantSellerRef, and an authorization code can be exchanged only once even under concurrent requests. POST /v1/sellers/{id}/unlink is documented.
- Breaking: a Space API key now sees and books only its own Business’s Spaces (another Business’s Space is 404). Each key has a bookingMode: INTERNAL_FREE (default) makes free, confirmed internal reservations and returns bookingUrl; CHARGE_BOOKER keeps the hosted payment flow. Keys can list (GET /v1/space/bookings, with from and to) and cancel the bookings they made.
- Space drop-offs: the person dropping off and the person picking up add the photos (GET /v1/space/bookings/{id}/handover, POST …/handover/photos); the host only checks PINs, and verify-dropoff-pin / verify-pickup-pin no longer take a photo. All three parties see the photos and timeline and are emailed at each step.
- Unlinking a seller (POST /v1/sellers/{id}/unlink) takes that store’s live listings for the seller down, and is refused (409) while a trade through the store is in progress.
- GET /v1/platform/pricing returns what TBBN charges in a country — plans, usage rates, top-ups and Space fees — in its own currency where TBBN has set prices there, USD otherwise. GET /v1/platform/countries/{country} now includes currency, the currency listings and Space prices there are in.
- Local currencies: where TBBN is live in a country with its own prices, plans, usage, top-ups and fees are billed in that currency. A Business keeps the currency of its first charge; GET /v1/billing/pricing/{businessId} shows its prices, and billing responses carry currency (their …Usd amounts are in it).
- A member’s listing, a Space price and a new Merchant take their country’s currency: currency on POST /v1/listings/individual, feeCurrency on Space pricing and defaultCurrency on POST /v1/merchants are ignored. Merchant listings accept any ISO 4217 code.
- Listings are never converted between currencies: an offer mixing currencies is refused (400 OFFER_CURRENCY_MISMATCH), and match suggestions only include listings in the same currency.
- The TBBN blog: GET /v1/blog/posts (sort, category, tag, search), /v1/blog/posts/{slug}, its comments and /v1/blog/taxonomy are public; members like (PUT/DELETE …/like) and comment with a session.
2026-10-02
Help Center, team roles, meeting places, Merchant applications and live keys
- Team roles: Merchant teams can be ADMIN, DEVELOPER, ACCOUNT_MANAGER, OPERATIONS, ACCOUNTANT, SUPPORT or VIEWER, and DELETE /v1/merchants/{id}/users/{userId} removes someone. Business teams add OPERATIONS, ACCOUNTANT, SUPPORT and VIEWER, and OPERATIONS, SUPPORT, RECEPTION and VIEWER can be limited to some locations with branchIds. OWNER can no longer be assigned. See Team roles.
- GET /v1/businesses lists Businesses you own and those you’re on the team of, with myRole and myBranchIds.
- POST /v1/trade-sessions/{id}/space-proposals takes spaceId, scheduledAt and acceptTerms: when the other trader accepts, the Space is booked in the proposer’s name. Breaking: accepting returns { proposal, booking }, and only the trader who didn’t propose can accept.
- A signed-in member can list and unlink the stores their account is linked to (GET /v1/sellers/me/links, DELETE /v1/sellers/me/links/{merchantId}).
- New Help Center at /help: step-by-step guides for accounts, trading, booking and hosting Spaces, Businesses and payouts, Merchant applications, API keys and webhooks.
- Breaking: POST /v1/merchants requires a session for the owner or an Admin of the Business named in businessId (401 without one, 403 for anyone else). Apply from the Business’s Linked merchants page on business.tbbnetwork.com.
- Production API keys (sk_live_) are issued once TBBN has approved the Merchant account; before that, creating one returns 409. Sandbox keys are unaffected. The Merchant dashboard now has a Create production key button.
- The Merchant dashboard shows each webhook endpoint’s signing secret, and asks for an emailed confirmation code before saving settings on an approved account.
2026-09-30
Matching-only settlement, safe retries, finishing trades while paused
- Send an Idempotency-Key on any POST, PUT, PATCH or DELETE and a retry with the same body gets the first response back (Idempotent-Replayed: true), a 5xx included. The same key with a different body is 409; keys last 24 hours. See API conventions for every outcome.
- Every error, including authentication, rate-limit and idempotency errors, uses the documented envelope: { error: { code, message, requestId, details? } } with an uppercase code. Every response carries X-Request-Id.
- Breaking: idempotency error codes are uppercase (IDEMPOTENCY_KEY_REUSE, IDEMPOTENCY_IN_PROGRESS) inside error.code. New: IDEMPOTENCY_OUTCOME_UNKNOWN.
- Payment, fulfillment and matching-only settlement reports are safe to repeat: a report already recorded returns success and changes nothing; a contradicting one is 409.
- A matching-only side can extend its report deadline once, by 1 to 14 days, with POST /v1/trade-sessions/{id}/external-settlement/extend. New event: trade.external_settlement_extended.
- Members can edit listings they created themselves with PUT /v1/listings/individual/{id} (not while reserved in a trade). A merchant’s listing changes only where the merchant listed it.
- The Merchant reference documents matchingEnabled and checkoutEnabled, their defaults, and who can change them.
- Each side of a trade follows its own merchant’s setting: a matching-only side (checkoutEnabled false) settles on its own platform and reports once with POST /v1/trade-sessions/{id}/external-settlement, while the other side keeps TBBN checkout. Unreported sides are released after 14 days.
- New events: trade.external_settlement_started and trade.external_settlement_reported. Payment rows report settlement and externalSettlementDueAt.
- While production access is paused, live keys can still read and finish or unwind existing trades — payment and fulfillment reports, cancellations, closing, offer rejections, taking listings down. Starting new activity still returns 402.
- Merchants listing only for reach (matchingEnabled false, or SALE listings) have no sales to report; they keep each listing’s status current through the API or their feed.
- Breaking: listing API keys or OAuth clients requires merchantId and a credential for that merchant. The audit-log resource is no longer part of the public API.
- Catalog feeds accept external_seller_id on each row, so one feed can carry a multi-seller catalog. A row naming a seller you haven’t linked is skipped and reported in the sync result.
- Breaking: a merchant listing’s sellerId, and a feed’s defaultSellerId, must be one of your own sellers (linked or headless) — 403 SELLER_NOT_LINKED otherwise. A listing reserved in a trade keeps its seller (409 LISTING_RESERVED).
2026-09-29
Counterparty hold, webhook retries, feed delisting and SDKs
- The counterparty hold is 14 days by default; a Merchant can set counterpartyHoldDays from 7 to 14 (any other value is rejected with 400). A hold’s length is fixed when it starts.
- Webhooks that fail are retried after 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours before the dead-letter queue. Deliveries report nextAttemptAt.
- Catalog feeds take a listing down when its item is out of stock (availability or a quantity of 0) or no longer in the feed, and put it back when it returns. Listings created through the API are never affected.
- @tbbn/sdk-js and @tbbn/sdk-typescript 0.5.0; the React, Next.js, Vue and React Native packages 0.3.0.
- The Python, .NET, Java, Android, Flutter, PHP and Swift SDKs 0.3.0: per-Business billing and the usage balance, Businesses, Branches and Space bookings, OAuth seller linking, reviews, status and Business webhooks.
- Every SDK now surfaces the API’s own error message and details instead of a generic HTTP status.
- Breaking: billing methods take a Business id, and the email-then-phone seller-linking methods are removed — link sellers with sellers/verify and the OAuth flow.
2026-09-28
Plans, usage balance and Space policies
- Starter is $50/mo or $450/yr and Growth is $125/mo or $1,200/yr, before tax; plans are bought through Stripe Checkout.
- Each plan includes a monthly allowance per service. Usage past it, and every checkout-orchestration fee, is paid from a prepaid usage balance (GET /v1/billing/wallet/{businessId}, POST …/top-up). A balance below zero returns 402 on live keys until you top up or upgrade.
- Allowance and low-balance alerts, and a usage breakdown by day, merchant or service.
- TBBN Space bookings show booking terms before payment: the host’s refund and no-show policy, a processing fee and tax. Host payouts are made after a dispute window.
- Organizations with members-only listings; listings, directory and search accept country and organizationId.
- Report a listing, member, Space, booking or message; listings are screened for prohibited items on every write path, including feeds.
- GET /v1/status — live status of every product area, no credential needed.
2026-09-22
Scheduled catalog feeds
- Point an XML, CSV or TSV product feed at TBBN (POST /v1/merchant/feed-source); it is pulled every 24 hours or on demand.
2026-09-20
Uploads, step-up verification and account data
- Presigned direct uploads for listing and Space photos.
- Step-up verification for sensitive changes to a verified Business, Merchant or Space.
- Account data export and self-service account deletion.
- Currency, country and phone fields are validated server-side (ISO codes, E.164).
2026-09-16
Independent fulfillment and the counterparty hold
- Each side of a trade checks out and fulfills on its own; neither waits for the other. The fulfillment webhook requires a side, and a trade completes once both sides have fulfilled.
- Once one side has paid, the other side’s listing is held for the counterparty hold; after it the waiting side may close the trade with a note.
- Pay-As-You-Go billing, charged weekly at max($5, that week’s usage).
- Trade-scoped messaging between the two sellers of a trade.
- Breaking: webhook subscription and delivery routes derive ownership from your credential, and listing deliveries requires a subscription id.
v1.0.0
Platform foundation
- Auth: merchant-rep and member OTP and magic-link sign-in, refresh rotation, API keys.
- Merchant onboarding: application review and approval.
- Seller identity across merchants.
- Listing exchange, search, and matching across merchants.
- Offers, trade sessions, and checkout orchestration.
- Webhooks, usage-based billing, and this documentation site plus the SDKs.
