TBBN.Merchant Platform docs

API Reference

TBBN Platform API — v0.1.0

Trade By Barter Network infrastructure API. This document fully specifies the Phase 0-13 working surface: Auth, Merchants, API Keys, Sellers/Identity, Listings, Catalog, Media, Directory, Search, Trade Engine (settlement), Currency, Localization, Matching, Recommendations, Offers, Reservations, Trade Sessions, Checkout, Billing, Notifications, Webhooks, Audit, Moderation, Fraud, Reputation, Analytics, Feature Flags, and Sandbox — see docs/api/openapi-workflow.md.

Production: https://api.tbbnetwork.comSandbox: https://sandbox-api.tbbnetwork.comLocal dev (via api-gateway): http://localhost:3000

Live resources

Fully implemented — see the matching service's README for how it's tested.

All live endpoints

post/v1/auth/merchant/loginMerchant rep login (password or OTP)
post/v1/auth/merchant/otp/requestRequest email OTP for merchant rep login
post/v1/auth/merchant/otp/verifyVerify merchant rep OTP and issue a session
post/v1/auth/member/registerDirect self-registration for a Marketplace profile (Phase 22) — the primary way anyone joins TBBN now. See docs/business/conflict-resolution-log.md item 18.
post/v1/auth/member/otp/verifyComplete self-registration and issue a session
post/v1/auth/seller/otp/requestRequest OTP for a marketplace member's direct TBBN Exchange login
post/v1/auth/seller/otp/verifyVerify marketplace member OTP and issue a session
post/v1/auth/seller/magic-link/requestRequest a passwordless magic link for a marketplace member
get/v1/auth/seller/magic-link/consumeConsume a magic link token, issuing a session
post/v1/auth/refreshRotate a refresh token for a new access token
post/v1/auth/logoutRevoke the current session
get/v1/auth/meResolve the current principal from the bearer token
post/v1/businessesCreate a Business, owned by the authenticated Marketplace member
get/v1/businessesList the authenticated Marketplace member's own Businesses
get/v1/businesses/{id}Get a Business
patch/v1/businesses/{id}Update a Business (owner only)
delete/v1/businesses/{id}Soft-delete a Business (owner only)
get/v1/businesses/pending-verificationOps-only in practice (Phase 34, fronted by apps/ops-console); no RBAC guard yet — see docs/security/rbac-strategy.md.
post/v1/businesses/{id}/verificationUnauthenticated by design (Phase 34) — interim ops-console review action; see BusinessesService.setVerification's doc comment.
post/v1/businesses/{businessId}/business-usersInvite a team member with a role (ADMIN+, Phase 34)
get/v1/businesses/{businessId}/business-usersList a Business's team (ADMIN+)
patch/v1/businesses/{businessId}/business-users/{userId}Change a team member's role (ADMIN+)
delete/v1/businesses/{businessId}/business-users/{userId}Remove a team member (ADMIN+; cannot remove the owner)
post/v1/businesses/{businessId}/branchesAdd a Branch (location) to a Business (owner only)
get/v1/businesses/{businessId}/branchesList a Business's Branches
patch/v1/branches/{id}Update a Branch (owner only)
delete/v1/branches/{id}Remove a Branch (owner only)
post/v1/branches/{id}/space-statusToggle a Branch's Space eligibility flag (owner only). Phase 23 hook only — no Space product logic exists yet.
post/v1/businesses/{businessId}/merchant-linksLink a Merchant account to a Business (owner only, many-to-many)
get/v1/businesses/{businessId}/merchant-linksList a Business's linked Merchant accounts
post/v1/businesses/{businessId}/merchant-links/{linkId}/revokeRevoke a Business<->Merchant link (owner only)
get/v1/business-merchant-linksReverse lookup — a Merchant's linked Businesses. Deliberately not nested under /v1/merchants/* — that prefix is routed to merchant-service by api-gateway's ROUTES table (prefix match), so a route nested there would never reach business-service.
post/v1/reputation/reviewsSubmit a 1-5 star review of a Merchant or a Space Branch — must be tied to a real completed trade session or Space booking the reviewer was actually a party to.
get/v1/reputation/merchants/{id}/reviewsA Merchant's reviews plus aggregate rating
get/v1/reputation/branches/{id}/reviewsA Space Branch's reviews plus aggregate rating
get/v1/reputation/businesses/{id}/reviews-summaryAggregate rating rolled up across every Merchant/Branch linked to a Business (direct Postgres read, no live cross-service call).
post/v1/merchantsSubmit a merchant onboarding application (creates a 7-day temp account)
get/v1/merchants/{id}Get merchant
patch/v1/merchants/{id}Update merchant settings (ADMIN/OWNER only)
post/v1/merchants/{id}/submit-applicationSubmit or resubmit onboarding documents during the 7-day temp window
get/v1/merchants/{id}/application-statusTrack onboarding application status
post/v1/merchants/{id}/users/inviteInvite a developer or account-manager sub-user (ADMIN/OWNER only)
get/v1/merchants/{id}/usersList merchant org users
patch/v1/merchants/{id}/users/{userId}/roleChange a sub-user's role (OWNER only)
post/v1/api-keysCreate an API key (ADMIN/OWNER only; production keys require ADMIN+ approval)
get/v1/api-keysList API keys (never returns the full key, only the prefix)
post/v1/api-keys/{id}/rotateRotate a key (old key marked ROTATED, still valid for a grace window)
delete/v1/api-keys/{id}Revoke a key immediately
post/merchant/sellers/verifyMerchant-facing: register or match a seller after the MERCHANT has verified them. See docs/business/glossary.md "Identity federation" — sellers never call this directly.
post/merchant/sellers/headlessMerchant-facing: create a headless Seller (Phase 27) — a trade participant with no linked TBBN account, gated by the calling Merchant's plan tier.
post/v1/oauth-clientsRegister a Merchant's account-linking OAuth client (Phase 28)
get/v1/oauth-clientsList a Merchant's OAuth clients
post/v1/oauth-clients/{id}/rotate-secretRotate an OAuth client's secret — the clientId is unchanged
delete/v1/oauth-clients/{id}Revoke an OAuth client
get/v1/link/oauth/clients/{clientId}Public client metadata — used by apps/account-dashboard's consent screen to validate redirect_uri and render "Merchant X wants to link your TBBN profile" before showing Approve/Deny.
post/v1/link/oauth/consentStep 1 of the OAuth account-linking handshake (Phase 28) — the logged-in member approves. Creates a short-lived, single-use authorization code; the caller (the consent page) performs the actual redirect.
post/v1/link/oauth/tokenStep 2 of the OAuth account-linking handshake — the Merchant's backend exchanges the code for a link. No access/refresh token is issued; this only ever establishes a seller_merchant_links row. See docs/business/conflict-resolution-log.md item 22.
get/v1/sellers/{id}Resolve a marketplace member with all linked merchant accounts (internal/ops use)
post/v1/sellers/{id}/unlinkOps-only unlink of a merchant link (fraud/dispute remediation)
post/merchant/listingsCreate or sync a listing. Upserts on (merchantId, merchantListingRef) — a repeat call with the same ref updates the existing listing rather than creating a duplicate. See docs/api/merchant-integration-guide.md "Sync your listings".
put/merchant/listings/{id}Update a listing by id (partial — only supplied fields change)
delete/merchant/listings/{id}Soft-delete a listing
post/merchant/listings/{id}/availabilityReport an availability change. reservation-service is the primary caller for RESERVED during a trade session; merchants call this directly for SOLD/UNAVAILABLE from their own inventory changes outside of TBBN trades.
put/merchant/listings/{id}/wantsReplace a listing's wants array (full replace, not merge) — see docs/business/glossary.md "Wants"
get/v1/listings/{id}/wantsRead a listing's wants array
get/v1/listings/{id}Get a listing
get/v1/listingsDirectory-lite listing read across all visibilities the caller is entitled to — full ranked search lives in Search/Matching
post/v1/listings/individualSelf-service individual (non-Merchant) listing (Phase 26) — sellerId is resolved from the caller's own marketplace-member session, never the request body.
get/v1/catalog/categoriesList all top-level categories in the taxonomy
get/v1/catalog/categories/{category}/subcategoriesList subcategories under a category
get/v1/catalog/brandsList brands, optionally scoped to a category and/or subcategory
post/v1/media/ingestIngest listing images from https:// source URLs (jpg/jpeg/png/webp only, max 10 per call) into TBBN-hosted canonical URLs suitable for a listing's images array.
get/v1/directory/listingsPublic global-directory listing read. Only GLOBAL/BOTH-visibility listings ever appear here — PRIVATE and ENTERPRISE never do. See docs/business/glossary.md "Global vs Private vs Both vs Enterprise".
get/v1/directory/listings/{id}Get a single listing from the public global directory
post/v1/search/listingsPlain filtered/keyword listing search, no compatibility scoring (compatibility scoring lives in Matching). query does a case-insensitive contains match across title, brand, subcategory, and model; minTradeValue/maxTradeValue filter on the computed TradeValue = OriginalPrice + RequestedAmount - WillingToPay.
post/v1/trade-engine/settlementThe authoritative TradeValue/settlement computation — offer-service and trade-session-service call this rather than reimplementing the formula (TradeValue = OriginalPrice + RequestedAmount - WillingToPay per listing). All listings in a trade must share one currency; cross-currency settlement-of-record is not yet supported and is rejected outright.
get/v1/currency/supportedList supported currency codes
post/v1/currency/convertConvert an amount between supported currencies using static reference rates. Estimate only — never authoritative for settlement (see TradeEngine, which rejects cross-currency trades outright rather than trusting these rates).
get/v1/localization/countriesList supported countries
get/v1/localization/languagesList supported languages
post/v1/localization/normalize-phoneNormalize a phone number to a canonical format
post/v1/matching/candidatesBulk candidate ranking for a single listing — scores the network's candidate pool (subject to the same PRIVATE/GLOBAL/BOTH visibility boundary directory-service enforces) against the source listing and returns the top N by Trade Compatibility Score. See docs/architecture/matching-algorithm-spec.md.
post/v1/matching/scoreDirect pairwise Trade Compatibility Score for two known listings — used to preview compatibility before sending an offer
get/v1/recommendations/sellers/{sellerId}Personalized "for you" feed for a seller — scores the network's candidate pool against every active listing the seller owns and keeps each candidate's best score, so a candidate that's a great match for any one of the seller's listings surfaces once. See docs/architecture/matching-algorithm-spec.md.
post/v1/offersCreate an offer. listingIdsA is the existing listing(s) being offered ON, owned by toSellerId; listingIdsB is the listing(s) offered IN EXCHANGE, owned by fromSellerId — see docs/business/conflict-resolution-log.md item 3.
get/v1/offersList offers for a seller
get/v1/offers/{id}Get an offer. A PENDING offer past its expiresAt is lazily transitioned to EXPIRED on read.
post/v1/offers/{id}/acceptAccept a PENDING offer — only the recipient (toSellerId) may accept. Does not create a trade session directly; trade-session-service reacts to the resulting offer.accepted event.
post/v1/offers/{id}/rejectReject a PENDING offer — only the recipient (toSellerId) may reject
post/v1/offers/{id}/cancelCancel a DRAFT or PENDING offer — only the sender (fromSellerId) may cancel
post/v1/offers/{id}/counterCounter a PENDING offer — only the recipient may counter. Creates a brand-new offer with sender/recipient roles swapped and moves the original offer to COUNTERED; the two are linked only via the offer.countered event, not a foreign key.
post/v1/reservationsAll-or-nothing lock across every listing on both sides of a trade session — a side is not RESERVED until every listing in its set is locked; one unavailable listing fails the whole side. Called by trade-session-service when advancing ACCEPTED -> RESERVING. See docs/architecture/trade-session-state-machine.md "Multi-item trades".
get/v1/reservationsList reservations for a trade session
post/v1/reservations/releaseRelease every RESERVED reservation for a session back to ACTIVE — used on cancellation, expiry, or reservation/checkout failure. Only touches listings still RESERVED.
get/v1/trade-sessionsList trade sessions for a seller (as either sellerA or sellerB). Trade sessions are always system-generated from an accepted offer, never created directly via this API.
get/v1/trade-sessions/{id}Get a trade session
post/v1/trade-sessions/{id}/cancelCancel a trade session — only a party to the trade (sellerA or sellerB) may cancel, and only while the session is before LOCKED/FULFILLMENT/FULFILLED/COMPLETED/CANCELLED/ RETURNED/REFUNDED. Releases any RESERVED reservations as a side effect.
post/v1/trade-sessions/{id}/scheduling/confirmPeer-to-peer trade completion (Phase 26) — either party confirms the exchange happened on their side. Once both sides have confirmed, the session moves to COMPLETED.
post/v1/trade-sessions/{id}/scheduling/failEither party reports the peer-to-peer exchange failed (no-show, item mismatch)
post/v1/checkout/webhooks/paymentMerchant-facing payment status callback (mirrors the payment.completed/payment.failed webhook catalog entries). Reported completion of both sides' payments drives the trade session to LOCKED.
post/v1/checkout/webhooks/fulfillmentMerchant-facing fulfillment status callback — started requires the session be LOCKED, completed requires FULFILLMENT
post/v1/checkout/trade-sessions/{id}/completeOps-triggered final completion once a trade session is FULFILLED — no automated signal exists yet for "both merchants confirm no open disputes/returns window", so this is the explicit manual interface for that step.
get/v1/checkout/paymentsGet both sides' payment records for a trade session
post/v1/billing/subscriptionsCreate a Business's SaaS subscription
get/v1/billing/subscriptions/{businessId}Get a Business's current active SaaS subscription
post/v1/billing/subscriptions/{businessId}/change-tierChange a Business's subscription tier
post/v1/billing/subscriptions/{businessId}/cancelCancel a Business's subscription
post/v1/billing/usageRecord a metered usage event for a merchant (internal — called by other services, not typically by merchants directly)
get/v1/billing/usage/{businessId}A Business's current (or specified) billing-period metered usage, aggregated across every Merchant linked to it, including the cross-merchant multiplier breakdown and priced overage per event type. See docs/business/pricing.md.
get/v1/billing/statements/{businessId}Consolidated Business billing statement — merchantPlan (owed) and spaceEarnings (already paid out, net of commission) as two separate sections, never merged into one number. See docs/business/conflict-resolution-log.md item 20.
get/v1/notificationsList a marketplace member's in-app notifications. Every Seller has a User from creation (Phase 22), so this reaches every seller directly, in addition to their merchant notifying them through the merchant's own systems via Webhooks. See docs/business/glossary.md "Identity federation".
post/v1/notifications/{id}/readMark a notification read
post/v1/webhooks/subscriptionsSubscribe to events. events is an array of event-type strings (e.g. offer.accepted) or ["*"] for every event. See docs/architecture/event-webhook-catalog.md for the full event/webhook catalog. The signing secret is returned once, at creation time.
get/v1/webhooks/subscriptionsList a merchant's webhook subscriptions
post/v1/webhooks/subscriptions/{id}/disableDisable a webhook subscription (must be owned by the calling merchant)
get/v1/webhooks/deliveriesList delivery attempts, optionally scoped to one subscription
post/v1/webhooks/deliveries/{id}/replayReplay a delivery — must be idempotent on the merchant's receiving end, since the original attempt(s) may also have succeeded after all retries appeared exhausted.
post/v1/webhooks/business-subscriptionsRegister a Business-rooted webhook subscription (Phase 34) — fires on Space booking lifecycle events (space_booking.created/consent_required/consented/paid/cancelled/completed).
get/v1/webhooks/business-subscriptionsList a Business's webhook subscriptions
post/v1/webhooks/business-subscriptions/{id}/disableDisable a Business webhook subscription
get/v1/webhooks/business-deliveriesList Business webhook deliveries, optionally scoped to a subscription
post/v1/webhooks/business-deliveries/{id}/replayReplay a Business webhook delivery
get/v1/audit-logsList audit log entries, optionally scoped to a target
get/v1/moderation/flagsList moderation flags. listing-service's synchronous scanner already blocks a flagged write at the moment of sync — a flagged listing never reaches Postgres. These rows are a record of rejected attempts, useful for tuning the pattern list and handling merchant appeals.
get/v1/moderation/flags/{id}Get a moderation flag
post/v1/moderation/flags/{id}/reviewOps review of a FLAGGED entry. Reviewing does not retroactively publish a listing — a confirmed false positive still requires the merchant to resubmit through Listings.
get/v1/fraud/signalsList fraud signals (ops/internal read). Signals are detected automatically from windowed event counts (e.g. rapid offer creation, repeated moderation violations) — there is no endpoint to create one directly.
get/v1/reputation/sellers/{id}Get a seller's reputation score (0-100). New sellers start at the neutral baseline of 50 — the same default @tbbn/core-matching's reputationMatch factor uses when reputation data is absent, so Matching and Reputation never disagree about what "no track record yet" means.
get/v1/analytics/overviewGMV, trade volume, offer-acceptance match rate, and completion rate over a date range (default: trailing 30 days), optionally scoped to one merchant via its listings' involvement in offers/trade sessions.
post/v1/featuresCreate or update a feature flag. Omit merchantId for a global (network-wide) flag; set it for a per-merchant override, which wins over the global value when both exist.
get/v1/featuresList feature flags — global flags merged with a merchant's overrides when merchantId is supplied
get/v1/features/{key}/checkCheck whether a single flag is enabled, resolving a merchant override over the global default
post/v1/sandbox/merchantsSelf-serve sandbox provisioning — no manual application/approval flow, unlike a real merchant. Every call creates a brand-new isolated sandbox merchant + seller + API key, so trying the API never touches another developer's sandbox data. The API key is shown once.
get/v1/sandbox/fixturesCanned example request payloads (listing sync, offer create, matching candidates) for docs-site's live "try it" widgets — real shapes matching each service's actual DTOs

Planned resources

Documented ahead of implementation so the target surface is visible — not yet live. See the OpenAPI workflow guide for how a resource graduates from here into the live spec above.