{"openapi":"3.1.0","info":{"title":"TBBN Platform API","version":"0.1.0","description":"TBBN infrastructure API — the identity federation, listing exchange, matching, offer/trade-session management, and checkout-orchestration surface your platform integrates against: Auth, Merchants, Businesses, API Keys, Sellers/Identity, Listings, Catalog, Media, Directory, Search, Trade Engine (settlement), Currency, Localization, Matching, Recommendations, Offers, Trade Sessions, Checkout, Billing, Notifications, Webhooks, Reputation, Analytics, Space, and Sandbox.\n","contact":{"name":"TBBN developer community","url":"https://developer.tbbnetwork.com/community"},"license":{"name":"MIT","url":"https://opensource.org/licenses/MIT"}},"servers":[{"url":"https://api.tbbnetwork.com","description":"Production — sandbox vs. live is determined by which API key prefix you present (sk_sandbox_... or sk_live_...), not a different host or URL."}],"tags":[{"name":"Auth"},{"name":"Businesses"},{"name":"Space"},{"name":"Merchants"},{"name":"ApiKeys"},{"name":"Sellers"},{"name":"Platform","description":"Launch state, what is available per country, and the agreements people accept."},{"name":"Blog","description":"The TBBN blog — posts, likes and comments, and writing for it."},{"name":"Listings"},{"name":"Catalog"},{"name":"Media"},{"name":"Directory"},{"name":"Search"},{"name":"TradeEngine"},{"name":"Currency"},{"name":"Localization"},{"name":"Matching"},{"name":"Recommendations"},{"name":"Offers"},{"name":"TradeSessions"},{"name":"Checkout"},{"name":"Messages"},{"name":"Billing"},{"name":"Notifications"},{"name":"Webhooks"},{"name":"Reputation"},{"name":"Analytics"},{"name":"Sandbox"}],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"sk_live_... / sk_sandbox_...","description":"Merchant API key (sk_live_… for production, sk_sandbox_… for sandbox)."},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Merchant-rep or federated-seller session JWT."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Optional on any POST, PUT, PATCH or DELETE made with a credential. A retry with the same key and the same body gets the first response back (header Idempotent-Replayed: true) instead of running again — a 5xx included, since the request may have taken effect. Errors (409 unless noted): IDEMPOTENCY_KEY_REUSE (same key, different body), IDEMPOTENCY_IN_PROGRESS (the first request is still running), IDEMPOTENCY_OUTCOME_UNKNOWN (the first request didn't finish cleanly — check the resource, then use a new key), IDEMPOTENCY_KEY_INVALID (400, key over 255 characters). A request that never reached TBBN's services (502 BAD_GATEWAY) isn't kept, so the same key can be retried. Keys are per credential owner, method and path, and kept 24 hours. See the API conventions guide."},"Cursor":{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},"Limit":{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25,"maximum":100}}},"schemas":{"BlogWriterProfile":{"type":"object","required":["role","penName","accountName"],"properties":{"role":{"type":"string","enum":["AUTHOR","EDITOR"]},"penName":{"type":["string","null"],"description":"The name the public sees on your posts and comments; null means your account name."},"accountName":{"type":["string","null"],"description":"Your account's name","shown only to you":null}}},"BlogPostCard":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"title":{"type":"string"},"excerpt":{"type":"string"},"coverImageUrl":{"type":["string","null"]},"coverImageAlt":{"type":["string","null"]},"category":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"publishedAt":{"type":"string","format":"date-time"},"featured":{"type":"boolean"},"readingMinutes":{"type":"integer"},"likeCount":{"type":"integer"},"commentCount":{"type":"integer"},"viewCount":{"type":"integer"},"shareCount":{"type":"integer"},"author":{"type":"object","properties":{"name":{"type":"string"}}}}},"BlogPostInput":{"type":"object","description":"Every field optional when editing. The body is Markdown.","properties":{"title":{"type":"string","minLength":3,"maxLength":160},"slug":{"type":"string","maxLength":80,"description":"Made from the title if left out."},"excerpt":{"type":"string","maxLength":320},"body":{"type":"string"},"coverImageUrl":{"type":["string","null"],"format":"uri"},"coverImageAlt":{"type":["string","null"]},"category":{"type":"string"},"tags":{"type":"array","maxItems":10,"items":{"type":"string"}},"seoTitle":{"type":["string","null"],"maxLength":70},"seoDescription":{"type":["string","null"],"maxLength":170},"featured":{"type":"boolean","description":"Editors only."}}},"CountryCapabilities":{"type":"object","properties":{"country":{"type":"string"},"identityVerification":{"type":"boolean"},"businessVerification":{"type":"boolean"},"merchantOnboarding":{"type":"boolean"},"spaceHosting":{"type":"boolean"},"acceptPayments":{"type":"boolean"},"payouts":{"type":"boolean"},"marketplaceListings":{"type":"boolean"},"currency":{"type":"string","description":"ISO 4217 code that listings and Space prices in this country are in."},"formConfig":{"type":"object","additionalProperties":true,"description":"Labels and required fields for this country's forms."}}},"Agreement":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["BUSINESS_PROFILE","MERCHANT","SPACE_HOST","TRADE_TERMS"]},"version":{"type":"integer"},"title":{"type":"string"},"summary":{"type":["string","null"]},"body":{"type":"string","description":"Markdown"},"effectiveAt":{"type":"string","format":"date-time"}}},"IdentityStatus":{"type":"object","properties":{"status":{"type":"string","enum":["UNVERIFIED","PENDING","VERIFIED","NEEDS_ATTENTION"]},"verifiedAt":{"type":["string","null"],"format":"date-time"},"country":{"type":["string","null"]},"verificationAvailable":{"type":"boolean","description":"Whether the country access matrix offers identity verification where the person lives."},"configured":{"type":"boolean","description":"Whether this deployment has a verification provider at all."},"latest":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING","VERIFIED","NEEDS_ATTENTION"]},"lastError":{"type":["string","null"],"description":"A plain sentence saying what to fix, when the last attempt could not be completed."},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time"}}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","requestId"],"properties":{"code":{"type":"string","example":"NOT_FOUND","description":"Stable, machine-readable, SCREAMING_SNAKE_CASE. Branch on this, not on message."},"message":{"type":"string","description":"For people; wording may change."},"requestId":{"type":"string","example":"req_8f3a1c2b9d4e5f60718293a4","description":"Same value as the response's X-Request-Id header. Quote it to support."},"details":{"type":"object","description":"Present only when there is structured context, e.g. validation messages."}}}}},"OpeningHours":{"type":"object","description":"Opening hours, Sunday to Saturday, in an IANA time zone. A day is open with an opening and closing time (\"HH:MM\", 24-hour) or closed. A closing time at or before the opening time runs past midnight; 00:00 to 24:00 is open all day.","required":["timeZone","days"],"properties":{"timeZone":{"type":"string","example":"America/Denver"},"days":{"type":"array","minItems":7,"maxItems":7,"items":{"type":"object","required":["day","open","opensAt","closesAt"],"properties":{"day":{"type":"integer","minimum":0,"maximum":6,"description":"0 = Sunday."},"open":{"type":"boolean"},"opensAt":{"type":"string","example":"09:00"},"closesAt":{"type":"string","example":"17:00"}}}}}},"Business":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"ownerUserId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["ACTIVE","SUSPENDED","DELETED"]},"type":{"type":"string","enum":["INDIVIDUAL","REGISTERED"],"description":"Immutable after creation. Only REGISTERED can create a Merchant."},"legalName":{"type":["string","null"]},"registrationNumber":{"type":["string","null"]},"taxId":{"type":["string","null"]},"legalCountry":{"type":["string","null"],"description":"ISO 3166-1 alpha-2."},"logoUrl":{"type":["string","null"],"description":"Shown for the Business's Merchants and Spaces unless one sets its own."},"websiteUrl":{"type":["string","null"]},"supportEmail":{"type":["string","null"]},"supportPhone":{"type":["string","null"]},"privacyPolicyUrl":{"type":["string","null"],"description":"Used by the Business's Spaces that don't set their own."},"termsUrl":{"type":["string","null"],"description":"Used by the Business's Spaces that don't set their own."},"openingHours":{"oneOf":[{"$ref":"#/components/schemas/OpeningHours"},{"type":"null"}],"description":"Business hours. A Space can use them as its own (hoursSource BUSINESS) and follows any change. Null until set."},"verificationStatus":{"type":"string","enum":["PENDING","VERIFIED","REJECTED"],"description":"Only a VERIFIED, REGISTERED Business can create a Merchant. Starts PENDING and becomes VERIFIED once payout onboarding completes and the Business is reviewed."},"verificationReviewedAt":{"type":["string","null"],"format":"date-time"},"verificationReviewedBy":{"type":["string","null"]},"verificationNotes":{"type":["string","null"]},"connectAccount":{"$ref":"#/components/schemas/BusinessConnectAccount"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"BusinessConnectAccount":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"businessId":{"type":"string","format":"uuid"},"stripeConnectedAccountId":{"type":["string","null"]},"onboardingStatus":{"type":"string","enum":["NOT_STARTED","PENDING","COMPLETE"]},"detailsSubmitted":{"type":"boolean"},"country":{"type":["string","null"]},"businessType":{"type":["string","null"],"enum":["individual","company",null]},"representativeMatchedAt":{"type":["string","null"],"format":"date-time","description":"Set for an INDIVIDUAL Business once Stripe's representative matches the owner's verified identity."},"representativeMismatch":{"type":["string","null"],"description":"Set when the account's representative doesn't match the Business owner on file. Informational only."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"BusinessInput":{"type":"object","required":["name","type"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["INDIVIDUAL","REGISTERED"]},"legalName":{"type":"string","description":"Required when type is REGISTERED."},"registrationNumber":{"type":"string"},"taxId":{"type":"string"},"legalCountry":{"type":"string","description":"ISO 3166-1 alpha-2."}}},"Branch":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"businessId":{"type":"string","format":"uuid"},"name":{"type":"string"},"addressLine1":{"type":"string"},"addressLine2":{"type":["string","null"]},"city":{"type":"string"},"region":{"type":["string","null"]},"postalCode":{"type":["string","null"]},"country":{"type":"string"},"spaceStatus":{"type":"string","enum":["NOT_ENABLED","SPACE_ENABLED"],"description":"Hook only — no Space product logic exists yet."},"createdAt":{"type":"string","format":"date-time"}}},"BranchInput":{"type":"object","required":["name","addressLine1","city","country"],"properties":{"name":{"type":"string"},"addressLine1":{"type":"string"},"addressLine2":{"type":"string"},"city":{"type":"string"},"region":{"type":"string"},"postalCode":{"type":"string"},"country":{"type":"string"},"latitude":{"type":"number","description":"With longitude, the exact location when you have it; otherwise TBBN finds it from the address."},"longitude":{"type":"number"}}},"BusinessMerchantLink":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"businessId":{"type":"string","format":"uuid"},"merchantId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["ACTIVE","REVOKED"]},"linkedAt":{"type":"string","format":"date-time"},"unlinkedAt":{"type":["string","null"],"format":"date-time"}}},"SpaceServicePricing":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"spaceId":{"type":"string","format":"uuid","description":"The Space this price applies to."},"serviceType":{"type":"string","enum":["MEETUP","DROPOFF_PICKUP","WORKSPACE"]},"feeAmount":{"type":"number"},"feeCurrency":{"type":"string"},"maxHoldDays":{"type":["integer","null"]},"perDayFee":{"type":["number","null"]},"pricingUnit":{"type":"string","enum":["PER_BOOKING","PER_HOUR","PER_DAY"],"description":"WORKSPACE only — how feeAmount is multiplied."},"minDurationMinutes":{"type":["integer","null"],"description":"WORKSPACE only — bounds on a single booking's length."},"maxDurationMinutes":{"type":["integer","null"]},"requiresApproval":{"type":"boolean","description":"WORKSPACE only. When true, a direct booking of this service goes to AWAITING_APPROVAL instead of straight to PENDING_PAYMENT; a headless (API key) booking is unaffected."},"updatedAt":{"type":"string","format":"date-time"}}},"Space":{"type":"object","description":"The actual bookable room within a Branch — \"study rooms/meeting rooms/office rooms/etc.\" A Branch can list several.\n","properties":{"id":{"type":"string","format":"uuid"},"branchId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":["string","null"]},"category":{"type":"string","enum":["STUDY_ROOM","MEETING_ROOM","OFFICE_ROOM","EVENT_SPACE","RESTAURANT_TABLE","RETAIL_CORNER","PARKING_SPOT","COWORK","OTHER"],"description":"\"Airbnb for Space\" browsable category, purely a display/filter label.\n"},"checkInMethod":{"type":"string","enum":["SELF_SERVICE_NUMBERED","SIGNAGE","STAFF_ASSISTED","OTHER"],"description":"— how a guest identifies/finds their reserved spot on arrival."},"parkingAvailable":{"type":"boolean"},"parkingNotes":{"type":["string","null"]},"weeklyHours":{"type":"array","description":"[] means always bookable. Shape: { day: 0-6, opensAt, closesAt }[].","items":{"type":"object"}},"maxConcurrentBookings":{"type":"integer","description":"Spots: how many bookings can run at the same time. A booking is refused when every spot is taken for its time, and each booking is given its own spot (spotNumber)."},"hoursSource":{"type":["string","null"],"enum":["BUSINESS","CUSTOM",null],"description":"Where the Space's opening hours come from — its Business's hours (BUSINESS) or its own (CUSTOM, in openingHours). Required to finish setting up a Space."},"openingHours":{"oneOf":[{"$ref":"#/components/schemas/OpeningHours"},{"type":"null"}]},"bookingLengthValue":{"type":["integer","null"],"description":"How long each booking lasts, in bookingLengthUnit. Required to finish setup."},"bookingLengthUnit":{"type":["string","null"],"enum":["MINUTES","HOURS","DAYS","WEEKS","MONTHS",null],"description":"At most 1440 minutes, 24 hours, 31 days, 52 weeks or 12 months (calendar months — a year at most). Each booking's end is its start plus this length."},"spotLabel":{"type":["string","null"],"description":"What each spot is called (\"Table\", \"Desk\", \"Room\"). Null means \"Spot\"."},"defaultBookingDurationMinutes":{"type":"integer","description":"Assumed MEETUP slot length for the capacity check only."},"photos":{"type":"array","items":{}},"notes":{"type":["string","null"]},"visibility":{"type":"string","enum":["PUBLIC","API_ONLY","RESTRICTED_EMAIL"]},"allowedEmails":{"type":"array","items":{"type":"string"}},"allowedEmailDomains":{"type":"array","items":{"type":"string"}},"isFree":{"type":"boolean","description":"Drives a BusinessOneTimeCharge at creation time when true."},"paymentWaivedUntil":{"type":["string","null"],"format":"date-time","description":"When set, bookings are free for the trader until this time (distinct from isFree, the host's own choice); the host's pricing applies again afterwards. Set by TBBN."},"paymentWaiverReason":{"type":["string","null"]},"capacity":{"type":["integer","null"],"description":"Seats — for WORKSPACE bookings and listing display."},"amenities":{"type":"array","items":{"type":"string"}},"cancellationPolicy":{"type":"string","enum":["FLEXIBLE","MODERATE","STRICT"],"description":"Refund window if a trader cancels: FLEXIBLE (full refund until 24h before), MODERATE (48h), STRICT (no refund inside 7 days — the host is still paid, as if the exchange happened). Only takes effect on a booking with a scheduledAt."},"status":{"type":"string","enum":["DRAFT","ACTIVE","DISABLED"]},"wifiInstructions":{"type":["string","null"],"description":"Shown to a booker only once paid — never in a public listing."},"hostAcceptsLiability":{"type":"boolean","description":"Default false. If true, the host is liable for an item dropped off here, capped at liabilityCapAmount; if false, liability falls to the trader who dropped it off instead. Settable via PATCH /v1/space/spaces/{id}. Surface this before someone books a DROPOFF_PICKUP-capable Space — see the drop-off/pickup PIN-verify endpoints below for the required custody photos this pairs with."},"liabilityCapAmount":{"type":["number","null"],"description":"Only meaningful when hostAcceptsLiability is true. Settable via PATCH /v1/space/spaces/{id}."},"blackoutDates":{"type":"array","description":"{ start: \"YYYY-MM-DD\", end: \"YYYY-MM-DD\", label?: string }[]","items":{"type":"object"}},"bufferMinutes":{"type":"integer","description":"Minutes required between two scheduledAt-based bookings of this Space."},"minLeadTimeMinutes":{"type":"integer","description":"Minimum notice, in minutes, a booking must give before its scheduledAt."},"maxAdvanceDays":{"type":["integer","null"],"description":"How far ahead, in days, a booking may start. Null means no limit."},"icalToken":{"type":["string","null"],"description":"Backs this Space's public ICS calendar feed (GET .../calendar.ics?token=...). Generated once at creation, never rotated."},"externalCalendarUrl":{"type":["string","null"],"description":"A host's own calendar's ICS export URL, polled for busy windows."},"externalBusyRanges":{"type":"array","description":"{ start: ISO-8601, end: ISO-8601 }[] — parsed from the last successful sync.","items":{"type":"object"}},"lastSyncedAt":{"type":["string","null"],"format":"date-time"},"lastSyncError":{"type":["string","null"],"description":"Set only when the last external-calendar sync attempt failed."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"SpacePublicView":{"type":"object","description":"What `GET /v1/space/spaces/{id}` and `GET /v1/space/branches/{branchId}/spaces` return to anyone other than the Space's own host team: the public listing, including the host's name, location and pricing. Host-only settings such as calendar sync, Wi-Fi details and access lists are never included.","properties":{"id":{"type":"string","format":"uuid"},"branchId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":["string","null"]},"category":{"type":"string","enum":["STUDY_ROOM","MEETING_ROOM","OFFICE_ROOM","EVENT_SPACE","RESTAURANT_TABLE","RETAIL_CORNER","PARKING_SPOT","COWORK","OTHER"]},"checkInMethod":{"type":"string","enum":["SELF_SERVICE_NUMBERED","SIGNAGE","STAFF_ASSISTED","OTHER"]},"businessName":{"type":"string","description":"The host Business's name."},"businessVerificationStatus":{"type":"string","enum":["PENDING","VERIFIED","REJECTED"]},"city":{"type":"string","description":"From the owning Branch."},"region":{"type":"string","description":"From the owning Branch."},"country":{"type":"string","description":"From the owning Branch."},"postalCode":{"type":"string","description":"From the owning Branch."},"latitude":{"type":"number","description":"From the owning Branch."},"longitude":{"type":"number","description":"From the owning Branch."},"distanceMiles":{"type":["number","null"],"description":"Only set when the caller searched with lat/lng; null on a direct GET by id."},"parkingAvailable":{"type":"boolean"},"parkingNotes":{"type":["string","null"]},"weeklyHours":{"type":"array","description":"[] means always bookable. Shape: { day: 0-6, opensAt, closesAt }[].","items":{"type":"object"}},"openingHours":{"oneOf":[{"$ref":"#/components/schemas/OpeningHours"},{"type":"null"}],"description":"The hours the Space keeps (its own or its Business's); null when not set."},"bookingLength":{"type":["object","null"],"description":"How long each booking lasts.","properties":{"value":{"type":"integer"},"unit":{"type":"string","enum":["MINUTES","HOURS","DAYS","WEEKS","MONTHS"]}}},"spotLabel":{"type":["string","null"]},"maxConcurrentBookings":{"type":"integer"},"capacity":{"type":["integer","null"]},"photos":{"type":"array","items":{},"description":"Falls back to the owning Branch's own photos when this Space has none."},"amenities":{"type":"array","items":{"type":"string"},"description":"Falls back to the owning Branch's own amenities when this Space has none."},"notes":{"type":["string","null"],"description":"The host's own \"good to know\" message for guests (parking quirks, entry instructions), shown publicly."},"isFree":{"type":"boolean"},"visibility":{"type":"string","enum":["PUBLIC","API_ONLY","RESTRICTED_EMAIL"]},"cancellationPolicy":{"type":"string","enum":["FLEXIBLE","MODERATE","STRICT"]},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/SpaceServicePricing"}},"isPinned":{"type":"boolean","description":"Always false from a direct GET by id/branchId — only meaningful on a search result, where a caller's API-key config can pin certain Branches to the top."},"hostAcceptsLiability":{"type":"boolean"},"liabilityCapAmount":{"type":["number","null"]}}},"SpotBoardBooking":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"spotNumber":{"type":["integer","null"]},"who":{"type":["string","null"],"description":"The guest's or booker's first name."},"status":{"type":"string"},"checkedInAt":{"type":["string","null"],"format":"date-time"}}},"SpaceBooking":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"spaceId":{"type":"string","format":"uuid","description":"The Space booked."},"serviceType":{"type":"string","enum":["MEETUP","DROPOFF_PICKUP","WORKSPACE"]},"bookedByUserId":{"type":["string","null"],"format":"uuid","description":"Null for a headless (API-driven) booking — see bookedByBusinessId/ externalClientRef instead.\n"},"bookedByBusinessId":{"type":["string","null"],"format":"uuid","description":"Set instead of bookedByUserId for a headless booking — the consuming Business."},"externalClientRef":{"type":["string","null"],"description":"The consuming Business's own opaque identifier for their end client."},"spotNumber":{"type":["integer","null"],"description":"The spot this booking holds (1 to the Space's maxConcurrentBookings)."},"endsAt":{"type":["string","null"],"format":"date-time","description":"When the booking ends — its start plus the Space's booking length."},"consentRequired":{"type":"boolean","description":"— true when the headless path required an explicit consent step."},"consentToken":{"type":["string","null"],"description":"Unguessable — backs the hosted consent page. Only set when consentRequired."},"consentedAt":{"type":["string","null"],"format":"date-time"},"tradeSessionId":{"type":["string","null"],"format":"uuid","description":"Nullable forward-compat hook only — no code path sets this today."},"status":{"type":"string","enum":["AWAITING_CONSENT","AWAITING_APPROVAL","PENDING_PAYMENT","PAID","CONFIRMED","COMPLETED","CANCELLED","REFUNDED","NO_SHOW"],"description":"AWAITING_APPROVAL — a direct WORKSPACE booking whose Space requires host approval before payment (see SpaceServicePricing.requiresApproval); auto-cancelled if the host never responds within 24h. NO_SHOW — a paid WORKSPACE booking whose scheduled time passed with nobody checked in; settled under the host's no-show policy shown at booking."},"approvalDeadlineAt":{"type":["string","null"],"format":"date-time","description":"Set only when status is AWAITING_APPROVAL."},"recurringSeriesId":{"type":["string","null"],"format":"uuid","description":"Set on every occurrence created by POST /v1/space/bookings/recurring — links independent bookings sharing the same weekly schedule, not a separate series row."},"scheduledAt":{"type":["string","null"],"format":"date-time"},"holdUntil":{"type":["string","null"],"format":"date-time"},"durationMinutes":{"type":["integer","null"],"description":"WORKSPACE only — how long the room/desk is booked for."},"checkInCode":{"type":["string","null"],"description":"WORKSPACE only — shown to the guest, verified by the host/reception on arrival."},"checkedInAt":{"type":["string","null"],"format":"date-time"},"guestName":{"type":["string","null"]},"guestCompany":{"type":["string","null"]},"dropoffPhotoUrl":{"type":["string","null"],"description":"DROPOFF_PICKUP only. Set by POST /v1/space/bookings/{id}/verify-dropoff-pin — the record either side relies on if the item's condition at drop-off is later disputed. An image URL from POST /v1/media/upload-url."},"pickupPhotoUrl":{"type":["string","null"],"description":"DROPOFF_PICKUP only. Set by POST /v1/space/bookings/{id}/verify-pickup-pin, same convention as dropoffPhotoUrl."},"grossAmount":{"type":"number"},"currency":{"type":"string"},"commissionAmount":{"type":"number"},"hostPayoutAmount":{"type":"number"},"hostNotifiedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"confirmedAt":{"type":["string","null"],"format":"date-time"},"cancelledAt":{"type":["string","null"],"format":"date-time"},"noShowAt":{"type":["string","null"],"format":"date-time"}}},"SpaceSearchResult":{"type":"object","description":"One result row from GET /v1/space/search — one per Space, not per Branch.","properties":{"id":{"type":"string","format":"uuid","description":"The Space's own id — pass this as spaceId when booking."},"branchId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":["string","null"]},"category":{"type":"string","enum":["STUDY_ROOM","MEETING_ROOM","OFFICE_ROOM","EVENT_SPACE","RESTAURANT_TABLE","RETAIL_CORNER","PARKING_SPOT","COWORK","OTHER"]},"checkInMethod":{"type":"string","enum":["SELF_SERVICE_NUMBERED","SIGNAGE","STAFF_ASSISTED","OTHER"]},"businessName":{"type":"string"},"city":{"type":"string"},"region":{"type":["string","null"]},"country":{"type":"string"},"postalCode":{"type":["string","null"]},"latitude":{"type":["number","null"]},"longitude":{"type":["number","null"]},"distanceMiles":{"type":["number","null"],"description":"Null unless lat/lng were supplied (directly or via a Space API key's config)."},"parkingAvailable":{"type":"boolean"},"weeklyHours":{"type":"array","items":{"type":"object"}},"maxConcurrentBookings":{"type":"integer"},"capacity":{"type":["integer","null"]},"photos":{"type":"array","items":{}},"amenities":{"type":"array","items":{"type":"string"}},"isFree":{"type":"boolean"},"visibility":{"type":"string","enum":["PUBLIC","API_ONLY","RESTRICTED_EMAIL"]},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/SpaceServicePricing"}},"isPinned":{"type":"boolean","description":"True when this result's Branch is pinned in your Space API key's settings."}}},"TeamInvitation":{"type":"object","description":"An invitation to a Business's or Merchant's team. The person joins, with the role, when they accept it while signed in with the invited email (or account). Open for 14 days.\n","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":["string","null"],"format":"email"},"invitedUserId":{"type":["string","null"],"format":"uuid"},"role":{"type":"string"},"branchIds":{"type":"array","items":{"type":"string","format":"uuid"}},"status":{"type":"string","enum":["PENDING","ACCEPTED","DECLINED","REVOKED"]},"state":{"type":"string","enum":["PENDING","EXPIRED","ACCEPTED","DECLINED","REVOKED"],"description":"status, with an unanswered invitation past expiresAt shown as EXPIRED."},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"}}},"MyTeamInvitation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"team":{"type":"string","enum":["BUSINESS","MERCHANT"]},"teamId":{"type":"string","format":"uuid"},"teamName":{"type":"string"},"logoUrl":{"type":["string","null"]},"role":{"type":"string"},"invitedBy":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"}}},"BusinessUser":{"type":"object","description":"A designated team member on a Business, beyond its single owner.","properties":{"id":{"type":"string","format":"uuid"},"businessId":{"type":"string","format":"uuid"},"userId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["ADMIN","ACCOUNT_MANAGER","OPERATIONS","ACCOUNTANT","DEVELOPER","SUPPORT","RECEPTION","VIEWER"]},"branchIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The locations this person is limited to. Empty means every location."},"invitedBy":{"type":["string","null"],"format":"uuid"},"invitedAt":{"type":"string","format":"date-time"},"acceptedAt":{"type":["string","null"],"format":"date-time"}}},"BusinessWebhookSubscription":{"type":"object","description":"A Business's webhook: its Space booking events and, with includeMerchantEvents, the trade, offer, listing and checkout events of every Merchant the Business runs.\n","properties":{"id":{"type":"string","format":"uuid"},"businessId":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true,"description":"Your own name for the endpoint."},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"includeMerchantEvents":{"type":"boolean","description":"Also deliver the events of every Merchant the Business runs, including Merchants linked later. Those deliveries carry merchantIds — the Business's Merchants the event concerns.\n"},"secret":{"type":"string"},"status":{"type":"string","enum":["ACTIVE","DISABLED"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","nullable":true}}},"BusinessWebhookDelivery":{"type":"object","description":"Business-rooted counterpart to WebhookDelivery, same shape.","properties":{"id":{"type":"string","format":"uuid"},"subscriptionId":{"type":"string","format":"uuid"},"eventType":{"type":"string"},"payload":{"type":"object"},"status":{"type":"string","enum":["PENDING","DELIVERED","FAILED","DEAD_LETTER"]},"attempts":{"type":"integer"},"lastAttemptedAt":{"type":["string","null"],"format":"date-time"},"nextAttemptAt":{"type":["string","null"],"format":"date-time","description":"When a FAILED delivery is retried next; null once delivered or out of retries."},"createdAt":{"type":"string","format":"date-time"}}},"SpaceApiKey":{"type":"object","description":"A Business's credential for the Space discovery/booking API.","properties":{"id":{"type":"string","format":"uuid"},"businessId":{"type":"string","format":"uuid"},"keyPrefix":{"type":"string"},"fullKey":{"type":"string","description":"Shown only in the create/rotate response — never stored or returned again."},"environment":{"type":"string","enum":["SANDBOX","PRODUCTION"]},"status":{"type":"string","enum":["ACTIVE","ROTATED","REVOKED"]},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}},"SpaceApiKeyConfig":{"type":"object","description":"Filter defaults and \"primary results\" pinning for a SpaceApiKey — lets an integrator pre-configure default search filters and feature specific locations first in results.\n","properties":{"defaultCountry":{"type":["string","null"]},"defaultRegion":{"type":["string","null"]},"defaultRadiusMiles":{"type":["integer","null"]},"pinnedBranchIds":{"type":"array","items":{"type":"string","format":"uuid"}},"consentMode":{"type":"string","enum":["STRAIGHT","REQUIRE_CONSENT"],"description":"With bookingMode CHARGE_BOOKER: whether the person booked accepts the terms on a consent page before paying."},"bookingMode":{"type":"string","enum":["INTERNAL_FREE","CHARGE_BOOKER"],"default":"INTERNAL_FREE","description":"INTERNAL_FREE — bookings made with the key are free internal reservations of your own Spaces. CHARGE_BOOKER — the person booked pays on TBBN's hosted page."},"updatedAt":{"type":"string","format":"date-time"}}},"SpacePayout":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bookingId":{"type":"string","format":"uuid"},"branchId":{"type":"string","format":"uuid"},"amount":{"type":"number"},"currency":{"type":"string"},"status":{"type":"string","enum":["PENDING","PAID","FAILED"]},"stripeTransferId":{"type":["string","null"]},"paidAt":{"type":["string","null"],"format":"date-time"},"failedReason":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}}},"Review":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"reviewerId":{"type":"string","format":"uuid"},"targetType":{"type":"string","enum":["MERCHANT","SPACE_BRANCH"]},"merchantId":{"type":["string","null"],"format":"uuid"},"branchId":{"type":["string","null"],"format":"uuid"},"rating":{"type":"integer","minimum":1,"maximum":5},"comment":{"type":["string","null"]},"tradeSessionId":{"type":["string","null"],"format":"uuid"},"spaceBookingId":{"type":["string","null"],"format":"uuid"},"createdAt":{"type":"string","format":"date-time"}}},"PublicReview":{"type":"object","description":"A review as anyone can read it — the rating, the words, and the reviewer's first name.","properties":{"id":{"type":"string","format":"uuid"},"rating":{"type":"integer","minimum":1,"maximum":5},"comment":{"type":["string","null"]},"reviewerName":{"type":["string","null"],"description":"The reviewer's first name."},"createdAt":{"type":"string","format":"date-time"}}},"ReviewsList":{"type":"object","properties":{"reviews":{"type":"array","items":{"$ref":"#/components/schemas/PublicReview"}},"averageRating":{"type":["number","null"]},"reviewCount":{"type":"integer"}}},"Merchant":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"legalName":{"type":"string"},"displayName":{"type":"string"},"logoUrl":{"type":["string","null"],"format":"uri","description":"The store's logo — its public identifier on the listings it feeds, next to each seller's name. displayName is shown when there's no logo."},"slug":{"type":"string"},"tier":{"type":"string","enum":["SANDBOX","STARTER","GROWTH","ENTERPRISE"]},"status":{"type":"string","enum":["TEMP_PENDING","UNDER_REVIEW","APPROVED","SUSPENDED","REJECTED"]},"listingModeAllowed":{"type":"array","items":{"type":"string","enum":["PRIVATE","GLOBAL","BOTH","ENTERPRISE"]}},"matchingEnabled":{"type":"boolean","default":true,"description":"Whether this Merchant's listings can be matched and receive offers. false = discovery only (listings are visible but never matched or offered on). Settable via PATCH /v1/merchants/{id}."},"checkoutEnabled":{"type":"boolean","default":true,"description":"true = checkout orchestration: TBBN sends this Merchant's side of an accepted trade to its checkout and tracks the payment and fulfillment callbacks. false = matching only: this Merchant's side settles on its own platform and is reported once with POST /v1/trade-sessions/{id}/external-settlement within 14 days. Decided per side — the other side of a trade follows its own Merchant's setting. Applies to trades whose checkout opens after the change. Settable via PATCH /v1/merchants/{id}."},"autoCloseUnreciprocatedTrades":{"type":"boolean","description":"Opt-in, default false. When true, a trade session waiting on this Merchant's own checkout is automatically closed (CLOSED_UNRECIPROCATED) once the counterparty has held for the applicable period with no response — so this Merchant never has to remember to close one by hand. Settable via PATCH /v1/merchants/{id}."},"counterpartyHoldDays":{"type":["integer","null"],"description":"Days, 7 to 14; null means the 14-day default. The grace period this Merchant's seller gets to complete checkout after the other side of a trade has paid; their listing stays reserved for the trade meanwhile, and once it ends they may close the trade (or it closes automatically, with autoCloseUnreciprocatedTrades). Settable via PATCH /v1/merchants/{id}; any other value is rejected with 400. Only the waiting (unpaid) side's Merchant setting applies; when that side involves more than one Merchant, the longest of their values applies (the default for any Merchant without one). The length is fixed when a hold starts, so a change affects only holds that start afterward."},"commissionRate":{"type":["string","null"],"description":"Trade setup: the Merchant's commission on each of its items traded, as a percent of the item's listed price (0–100, up to two decimals; a decimal string). Null until set — the Merchant's items can't be offered or accepted for trade until it is, together with a trade checkout link. Every trader sees it in the trade quote before accepting, and an accepted trade keeps the rate it was accepted with."},"passCheckoutFee":{"type":"boolean","description":"Whether the trader checking out pays TBBN's checkout fee (otherwise the Merchant does)."},"passProcessingFees":{"type":"boolean","description":"Whether the Merchant adds its card processing fees at its trade checkout."},"shipFromRegion":{"type":["string","null"],"description":"The state or region the Merchant ships from, shown to traders for information."},"country":{"type":"string"},"defaultCurrency":{"type":"string","description":"ISO 4217. The currency of the Merchant's country (USD where TBBN isn't live there)."},"createdAt":{"type":"string","format":"date-time"},"approvedAt":{"type":["string","null"],"format":"date-time"}}},"MerchantApplicationInput":{"type":"object","description":"Everything but businessId defaults from the Business and the signed-in applicant — the legal name and country from the Business's legal details, the display name from its name, and the representative from the applicant. Send a field only when it differs.\n","required":["businessId"],"properties":{"repEmail":{"type":"string","format":"email"},"repName":{"type":"string"},"legalName":{"type":"string"},"displayName":{"type":"string"},"country":{"type":"string"},"defaultCurrency":{"type":"string","deprecated":true,"description":"Ignored. A Merchant's default currency is its country's."},"businessId":{"type":"string","format":"uuid","description":"Every Merchant belongs to a Business — create a REGISTERED Business first (POST /v1/businesses).\n"}}},"TradeSpaceProposal":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"tradeSessionId":{"type":"string","format":"uuid"},"branchId":{"type":"string","format":"uuid"},"spaceId":{"type":["string","null"],"format":"uuid"},"proposedBySellerId":{"type":"string","format":"uuid"},"scheduledAt":{"type":["string","null"],"format":"date-time"},"payerTermsAcceptedAt":{"type":["string","null"],"format":"date-time"},"bookingId":{"type":["string","null"],"format":"uuid"},"status":{"type":"string","enum":["PROPOSED","ACCEPTED","REJECTED","SUPERSEDED"]},"proposedAt":{"type":"string","format":"date-time"},"respondedAt":{"type":["string","null"],"format":"date-time"},"space":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"isFree":{"type":"boolean"}}},"branch":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"addressLine1":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"}}}}},"SpaceTestBooking":{"type":"object","description":"A booking made with a test Space API key. Same fields as a live booking's, with livemode false; taxAmount is always 0 and nothing is charged.\n","properties":{"id":{"type":"string","format":"uuid"},"livemode":{"type":"boolean","enum":[false]},"spaceId":{"type":"string","format":"uuid"},"serviceType":{"type":"string","enum":["MEETUP","DROPOFF_PICKUP","WORKSPACE"]},"status":{"type":"string"},"internal":{"type":"boolean"},"externalClientRef":{"type":["string","null"]},"scheduledAt":{"type":["string","null"],"format":"date-time"},"durationMinutes":{"type":["integer","null"]},"holdUntil":{"type":["string","null"],"format":"date-time"},"grossAmount":{"type":"number"},"processingFeeAmount":{"type":"number"},"taxAmount":{"type":"number"},"chargeAmount":{"type":"number"},"currency":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"cancelledAt":{"type":["string","null"],"format":"date-time"}}},"TestAccount":{"type":"object","properties":{"testMerchantId":{"type":["string","null"],"format":"uuid"},"sellers":{"type":"array","items":{"type":"object","properties":{"sellerId":{"type":"string","format":"uuid"},"merchantSellerRef":{"type":"string"},"name":{"type":"string"}}}}}},"SpaceHandover":{"type":"object","properties":{"bookingId":{"type":"string","format":"uuid"},"status":{"type":"string"},"myRole":{"type":"string","enum":["DEPOSITOR","RECIPIENT","HOST"]},"space":{"type":"object","properties":{"name":{"type":"string"},"locationName":{"type":"string"},"addressLine1":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"}}},"recipientName":{"type":["string","null"]},"recipientAccepted":{"type":["boolean","null"],"description":"Whether the person picking up accepted collecting from this Space (false = declined, null = not answered yet, or the person who booked is collecting). The item can't be dropped off until it's true.\n"},"recipientRespondedAt":{"type":["string","null"],"format":"date-time"},"canRespond":{"type":"boolean"},"canChangeRecipient":{"type":"boolean"},"holdUntil":{"type":["string","null"],"format":"date-time"},"droppedOffAt":{"type":["string","null"],"format":"date-time"},"pickedUpAt":{"type":["string","null"],"format":"date-time"},"dropoffPhotos":{"type":"array","items":{"type":"string","format":"uri"}},"pickupPhotos":{"type":"array","items":{"type":"string","format":"uri"}},"events":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["DROPOFF_PHOTO_ADDED","DROPPED_OFF","PICKUP_PHOTO_ADDED","PICKED_UP","RECIPIENT_ACCEPTED","RECIPIENT_DECLINED","RECIPIENT_CHANGED"]},"party":{"type":"string","enum":["DEPOSITOR","RECIPIENT","HOST"]},"photoUrl":{"type":["string","null"],"format":"uri"},"at":{"type":"string","format":"date-time"}}}},"dropoffPin":{"type":["string","null"],"description":"Only for the person dropping off."},"pickupPin":{"type":["string","null"],"description":"Only for the person picking up, once the item is dropped off."},"canAddDropoffPhotos":{"type":"boolean"},"canAddPickupPhotos":{"type":"boolean"}}},"MerchantUser":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"merchantId":{"type":"string","format":"uuid"},"userId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["OWNER","ADMIN","DEVELOPER","ACCOUNT_MANAGER","OPERATIONS","ACCOUNTANT","SUPPORT","VIEWER"]},"invitedAt":{"type":"string","format":"date-time"},"acceptedAt":{"type":["string","null"],"format":"date-time"}}},"ApiKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"merchantId":{"type":"string","format":"uuid"},"keyPrefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"environment":{"type":"string","enum":["SANDBOX","PRODUCTION"]},"status":{"type":"string","enum":["ACTIVE","ROTATED","REVOKED"]},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"}}},"ApiKeyCreated":{"allOf":[{"$ref":"#/components/schemas/ApiKey"},{"type":"object","properties":{"fullKey":{"type":"string","description":"Shown once, at creation time only. Never retrievable again."}}}]},"Seller":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"userId":{"type":["string","null"],"format":"uuid","description":"Null for a \"headless\" Seller — a merchant-tracked trade participant with no linked TBBN account at all.\n"},"primaryEmail":{"type":["string","null"]},"primaryPhone":{"type":["string","null"]},"reputationScore":{"type":["integer","null"]}}},"SellerVerifyInput":{"type":"object","required":["name","verifiedEmail","verifiedPhone","merchantSellerRef","merchantId"],"properties":{"name":{"type":"string","description":"The seller's name, nickname, or username — whichever the merchant is legally able to share. Never assumed to be a legal name; TBBN does not require or verify legal identity. See the Glossary guide \"Identity federation\".\n"},"verifiedEmail":{"type":"string","format":"email"},"verifiedPhone":{"type":"string"},"merchantSellerRef":{"type":"string"},"merchantId":{"type":"string","format":"uuid"}}},"SellerVerifyResult":{"type":"object","properties":{"sellerId":{"type":"string","format":"uuid"},"requiresConfirmation":{"type":"boolean","description":"True when the email/phone matched an existing seller under a different merchant merely knowing this contact info is not consent to link. The account owner must complete the OAuth account-linking handshake themselves at account.tbbnetwork.com/link/authorize — this response carries no confirmation mechanism of its own. item 22.\n"}}},"CreateHeadlessSellerInput":{"type":"object","required":["merchantId","merchantSellerRef"],"description":"Creates a \"headless\" Seller — a merchant-tracked trade participant with no linked TBBN account at all — gated by the calling Merchant's plan tier (Merchant.headlessSellersAllowed). No email/phone is collected; there is no existing account to match against.\n","properties":{"merchantId":{"type":"string","format":"uuid"},"merchantSellerRef":{"type":"string"},"name":{"type":["string","null"]}}},"OAuthClient":{"type":"object","description":"A Merchant's registered account-linking OAuth client.","properties":{"id":{"type":"string","format":"uuid"},"merchantId":{"type":"string","format":"uuid"},"clientId":{"type":"string","description":"Public identifier, e.g. mc_ab12cd34ef56ab78."},"clientSecret":{"type":"string","description":"Shown only in the create/rotate-secret response — never stored or returned again."},"redirectUris":{"type":"array","items":{"type":"string","format":"uri"}},"status":{"type":"string","enum":["ACTIVE","REVOKED"]},"createdAt":{"type":"string","format":"date-time"}}},"CreateOAuthClientInput":{"type":"object","required":["merchantId","redirectUris"],"properties":{"merchantId":{"type":"string","format":"uuid"},"redirectUris":{"type":"array","items":{"type":"string","format":"uri"}}}},"OAuthConsentInput":{"type":"object","required":["clientId","redirectUri","scope"],"description":"Called by TBBN's account consent screen once the logged-in marketplace member approves linking their account. userId/sellerId are resolved from the caller's own session, never this request body.\n","properties":{"clientId":{"type":"string"},"redirectUri":{"type":"string","format":"uri"},"scope":{"type":"string","example":"link:seller"},"codeChallenge":{"type":"string"},"codeChallengeMethod":{"type":"string","enum":["S256"]},"merchantSellerRef":{"type":"string","description":"Optional — the store's own reference for this customer, from merchant_seller_ref on the authorize link. Items the store listed with it as sellerRef become this member's."}}},"OAuthConsentResult":{"type":"object","properties":{"code":{"type":"string","description":"Single-use, 5-minute authorization code. The consent page itself performs the actual 302 redirect to redirectUri?code=...&state=... — this response is not the redirect.\n"}}},"OAuthTokenInput":{"type":"object","required":["grant_type","code","client_id","client_secret","redirect_uri"],"description":"Called by the Merchant's own backend — never a browser.","properties":{"grant_type":{"type":"string","enum":["authorization_code"]},"code":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"redirect_uri":{"type":"string","format":"uri"},"code_verifier":{"type":"string","description":"RFC 7636 PKCE — required if a codeChallenge was set at consent time."}}},"OAuthTokenResult":{"type":"object","properties":{"sellerId":{"type":"string","format":"uuid","description":"The customer's TBBN seller id. Store it with your customer and send it as sellerId on their listings (or as external_seller_id in your feed)."},"merchantSellerRef":{"type":"string","description":"Your reference for this customer, as you sent it in merchant_seller_ref — or, if you sent none, the one TBBN made from their seller id (it begins \"tbbn:seller:\")."},"linked":{"type":"boolean"},"attributedListings":{"type":"integer","description":"How many of your listings moved to this customer with the link — those you listed with their reference as sellerRef, or under the headless seller they replace."}}},"Want":{"type":"object","description":"Something the seller would swap the item for: an item, a vehicle, a home, a service or tickets, with the same kinds and fields a listing has. Matching compares the kind and category, subcategory and brand (case doesn't matter), each criterion in details, and the item's condition against minCondition. A listing of yours that sends no wants is matched on what your catalog offers.\n","properties":{"kind":{"type":"string","enum":["GOODS","VEHICLE","PROPERTY","SERVICE","TICKET"],"default":"GOODS"},"details":{"type":"object","additionalProperties":true,"description":"The kind's criteria — see the Listing kinds guide. Choices and names match the listing's field (make, fuel, propertyType, serviceType…); numbers are ranges written <field>Min / <field>Max (yearMin, mileageMax with mileageUnit, bedroomsMin).\n","example":{"make":"Toyota","yearMin":2015,"mileageMax":100000,"mileageUnit":"km"}},"category":{"type":"string","maxLength":100,"description":"Required for an item (GOODS); a vehicle, home, service or tickets set it."},"subcategory":{"type":["string","null"],"maxLength":100},"brand":{"type":["string","null"],"maxLength":100},"note":{"type":["string","null"],"maxLength":200,"description":"What exactly, e.g. \"iPhone 14, 128GB, unlocked\". No contact details."},"minCondition":{"type":["string","null"],"enum":["New","Used - Like New","Used - Good","Used - Fair","For Parts",null],"description":"The worst condition accepted; an item in worse condition matches this want only weakly.\n"},"imageUrl":{"type":["string","null"],"format":"uri","description":"A photo of the item wanted (https), handled like listing photos."}}},"TradeRecordItem":{"type":"object","properties":{"listingId":{"type":"string","format":"uuid"},"title":{"type":"string"},"merchantListingRef":{"type":["string","null"]},"originalPrice":{"type":"number"},"requestedAmount":{"type":["number","null"]},"willingToPay":{"type":["number","null"]},"tradeValue":{"type":"number","description":"originalPrice + requestedAmount − willingToPay."}}},"TradeRecord":{"type":"object","properties":{"tradeSessionId":{"type":"string","format":"uuid"},"status":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time"},"currency":{"type":"string"},"side":{"type":"string","enum":["A","B"]},"yourItems":{"type":"array","items":{"$ref":"#/components/schemas/TradeRecordItem"}},"yourOriginalPrice":{"type":"number"},"yourTradeValue":{"type":"number","description":"Your items' trade values, less any cash your trader added to the offer (added cash is paid on top, like willing to pay)."},"theirItems":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"originalPrice":{"type":"number"},"requestedAmount":{"type":["number","null"]},"willingToPay":{"type":["number","null"]},"tradeValue":{"type":"number"}}}},"theirOriginalPrice":{"type":"number"},"theirTradeValue":{"type":"number"},"counterparty":{"type":"object","description":"The other side's store by name, or an individual member (never named).","properties":{"kind":{"type":"string","enum":["MERCHANT","INDIVIDUAL"]},"name":{"type":"string"}}},"cashAddedToOffer":{"type":"number"},"youCollected":{"type":"number","description":"What the trader receiving your items paid at your checkout, before tax, processing fees and shipping — the difference they owed, plus your commission shortfall and any checkout fee you pass on."},"theyCollected":{"type":"number","description":"What your seller's trader paid at the other store's checkout."},"commissionRate":{"type":["number","null"],"description":"Your commission rate on this trade."},"commissionAmount":{"type":["number","null"],"description":"Your commission on your items' listed prices."},"commissionOffset":{"type":["number","null"],"description":"The part of your commission taken from the difference your seller was due."},"commissionShortfall":{"type":["number","null"],"description":"The part of your commission charged to the trader at your checkout."},"checkoutFeeCollected":{"type":["number","null"]},"sellerNetCash":{"type":["number","null"],"description":"What your seller is left with from the difference after commission."},"settlement":{"type":["string","null"],"enum":["TBBN_CHECKOUT","EXTERNAL",null]},"paymentStatus":{"type":["string","null"]},"paidAt":{"type":["string","null"],"format":"date-time"},"merchantCheckoutSessionRef":{"type":["string","null"]},"fulfilledAt":{"type":["string","null"],"format":"date-time"},"termsFrozen":{"type":"boolean","description":"False only for a trade whose checkout opened before item terms were recorded with the trade; its prices are then the listings' current ones."}}},"UpsertListingInput":{"type":"object","required":["merchantId","merchantListingRef","title","category","originalPrice","currency","visibility","listingType"],"properties":{"merchantId":{"type":"string","format":"uuid"},"merchantListingRef":{"type":"string","description":"The merchant's own identifier for this listing — the upsert key alongside merchantId."},"sellerId":{"type":"string","format":"uuid","description":"Optional. One of your own sellers — the sellerId a seller link returned, or a headless seller you created. Any other seller is 403 SELLER_NOT_LINKED. Leave it out for your store's own item (listed under your store's name), or send sellerRef instead. On an update, leaving both out keeps the listing's seller. A listing reserved in a trade keeps its seller until the trade ends (409 LISTING_RESERVED)."},"sellerRef":{"type":"string","maxLength":200,"description":"Optional, instead of sellerId. Your own reference for the customer this item belongs to — any stable value you choose; it never has to be your own customer id, and it is never shown publicly. Until that customer links their TBBN account the item is listed under your store; when they link with this same reference as merchant_seller_ref, it moves to them. References beginning \"tbbn:\" are reserved."},"title":{"type":"string"},"category":{"type":"string"},"subcategory":{"type":"string"},"brand":{"type":"string"},"model":{"type":"string"},"size":{"type":"string"},"condition":{"type":"string"},"year":{"type":"integer"},"originalPrice":{"type":"number"},"currency":{"type":"string","description":"ISO 4217. A listing keeps its own currency and is never converted; only listings in the same currency can be traded for one another.\n"},"visibility":{"type":"string","enum":["PRIVATE","GLOBAL","BOTH","ENTERPRISE"]},"listingType":{"type":"string","enum":["SALE","TRADE","BOTH"],"description":"Sale-only / trade-only / either. A SALE listing is never offer-able.\n"},"deliveryMethod":{"type":"string","enum":["LOCAL_PICKUP","SHIPPING","BOTH"]},"kind":{"type":"string","enum":["GOODS","VEHICLE","PROPERTY","SERVICE","TICKET"],"default":"GOODS","description":"What's listed. See the Listing kinds guide for each kind's details."},"details":{"type":"object","additionalProperties":true,"description":"The kind's own fields (a vehicle's make, model, year, mileage…; a home's offering, type, bedrooms…). Unknown fields are dropped; 422 LISTING_DETAILS_INVALID names any that are missing or wrong. A vehicle's make, model, type and year fill brand, model, subcategory and year, and every kind but goods sets its own category.\n"},"preferredSpaceId":{"type":"string","format":"uuid","description":"The TBBN Space suggested for the meetup or drop-off (an active, public Space). Members offering a swap can accept it; nothing is booked until both agree the deal.\n"},"preferredSpaceBranchId":{"type":"string","format":"uuid","description":"A seller's preferred TBBN Space, set at listing time."},"merchantItemUrl":{"type":"string","format":"uri","description":"The \"Google Merchant style\" outbound link shown on Landing-page for a SALE-only listing, in place of TBBN checkout.\n"},"requestedAmount":{"type":"number","description":"Cash asked for on top of the other item in a swap, at most the listed price. Mutually exclusive with willingToPay — a listing carries at most one of the two, never both. See the Glossary guide.\n"},"willingToPay":{"type":"number","description":"Cash the seller would add in a swap. It may be more than the listed price, which takes the item's trade value below zero. Mutually exclusive with requestedAmount.\n"},"note":{"type":"string"},"images":{"type":"array","maxItems":5,"items":{"type":"string","format":"uri"},"description":"Up to 5 photo URLs. The first is the cover photo."},"wants":{"type":"array","items":{"$ref":"#/components/schemas/Want"}}}},"CreateIndividualListingInput":{"type":"object","required":["title","category","originalPrice","listingType","images"],"description":"A member's own listing — no merchantId, merchantListingRef or sellerId (the seller is the signed-in member). It is priced in the currency of the member's country (USD where TBBN isn't live there) and keeps that currency. It needs at least one photo (422 PHOTO_REQUIRED) and, unless listingType is SALE, at least one want — something it would be swapped for (422 SWAP_WANTS_REQUIRED). A SALE listing's wants are cleared.\n","properties":{"title":{"type":"string"},"category":{"type":"string"},"subcategory":{"type":"string"},"brand":{"type":"string"},"model":{"type":"string"},"size":{"type":"string"},"condition":{"type":"string"},"year":{"type":"integer"},"originalPrice":{"type":"number"},"currency":{"type":"string","deprecated":true,"description":"Ignored. The listing takes the currency of the member's country."},"listingType":{"type":"string","enum":["SALE","TRADE","BOTH"]},"deliveryMethod":{"type":"string","enum":["LOCAL_PICKUP","SHIPPING","BOTH"],"deprecated":true,"description":"Ignored. A member's item is handed over locally (LOCAL_PICKUP)."},"kind":{"type":"string","enum":["GOODS","VEHICLE","PROPERTY","SERVICE","TICKET"],"default":"GOODS","description":"What's listed. See the Listing kinds guide for each kind's details."},"details":{"type":"object","additionalProperties":true,"description":"The kind's own fields (a vehicle's make, model, year, mileage…; a home's offering, type, bedrooms…). Unknown fields are dropped; 422 LISTING_DETAILS_INVALID names any that are missing or wrong. A vehicle's make, model, type and year fill brand, model, subcategory and year, and every kind but goods sets its own category.\n"},"preferredSpaceId":{"type":"string","format":"uuid","description":"The TBBN Space suggested for the meetup or drop-off (an active, public Space). Members offering a swap can accept it; nothing is booked until both agree the deal.\n"},"preferredSpaceBranchId":{"type":"string","format":"uuid"},"city":{"type":"string","maxLength":80,"description":"Where the item is (a city). Required — taken from the caller's connection when not sent. Shown on its card with region, e.g. \"Austin, TX\".\n"},"region":{"type":"string","maxLength":80,"description":"The state or region of city."},"latitude":{"type":"number","description":"Roughly where the item is; kept to about a kilometre and shown as an area."},"longitude":{"type":"number","description":"Roughly where the item is; kept to about a kilometre and shown as an area."},"requestedAmount":{"type":"number"},"willingToPay":{"type":"number"},"note":{"type":"string"},"images":{"type":"array","minItems":1,"maxItems":5,"items":{"type":"string","format":"uri"},"description":"One to five https image links. TBBN copies each into its own storage within minutes (resized to fit 1600px) and shows its copy; a link that can't be fetched or isn't an image is dropped. Links uploaded with POST /v1/media/upload-url are used as they are.\n"},"wants":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/Want"},"description":"What the item would be swapped for. Required unless listingType is SALE."}}},"Listing":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"merchantId":{"type":["string","null"],"format":"uuid","description":"Null for an individual (non-Merchant) listing."},"merchantListingRef":{"type":["string","null"]},"sellerId":{"type":"string","format":"uuid"},"title":{"type":"string"},"category":{"type":"string"},"subcategory":{"type":["string","null"]},"brand":{"type":["string","null"]},"model":{"type":["string","null"]},"size":{"type":["string","null"]},"condition":{"type":["string","null"]},"year":{"type":["integer","null"]},"originalPrice":{"type":"number"},"currency":{"type":"string"},"visibility":{"type":"string","enum":["PRIVATE","GLOBAL","BOTH","ENTERPRISE"]},"status":{"type":"string","enum":["ACTIVE","RESERVED","SOLD","UNAVAILABLE","DELETED"]},"listingType":{"type":"string","enum":["SALE","TRADE","BOTH"]},"deliveryMethod":{"type":["string","null"],"enum":["LOCAL_PICKUP","SHIPPING","BOTH",null]},"preferredSpaceBranchId":{"type":["string","null"],"format":"uuid"},"city":{"type":["string","null"],"description":"Where a person's own item is (their city). Not set on a store's listing."},"region":{"type":["string","null"],"description":"The city's state or region","e.g. TX.":null},"locationLabel":{"type":["string","null"],"description":"On public reads — where a person's own item is, as shown on cards (\"Austin, TX\"): its own city, else its meetup Space's. Null for a store's listing.\n"},"locationCountry":{"type":["string","null"],"description":"On public reads — the country the listing is in (ISO 3166-1, e.g. US): the store's for a store's listing, else where the person's item is. Show its name with the place to a viewer in another country, since a currency sign can mean different money in different countries.\n"},"sellerIdentityVerified":{"type":"boolean","description":"On public reads — the person behind a listing has verified their identity."},"publicNow":{"type":"boolean","description":"On the member's own create and edit responses, and to the member reading their own listing — whether anyone else can see it yet.\n"},"notPublicReason":{"type":["string","null"],"enum":["PENDING_REVIEW","NOT_LAUNCHED","NOT_ACTIVE",null],"description":"Why not: in review, TBBN not launched in the member's country yet, or marked sold or unavailable.\n"},"kind":{"type":"string","enum":["GOODS","VEHICLE","PROPERTY","SERVICE","TICKET"]},"details":{"type":"object","additionalProperties":true},"preferredSpaceId":{"type":["string","null"],"format":"uuid"},"sellerStoreSlug":{"type":["string","null"],"description":"On public reads — a person's own listing's storefront address."},"viewerIsOwner":{"type":"boolean","description":"On GET /v1/directory/listings/{id} — the signed-in member listed it. They can always read their own listing; anyone else gets 404 until it's public.\n"},"merchantItemUrl":{"type":["string","null"],"format":"uri"},"requestedAmount":{"type":["number","null"]},"willingToPay":{"type":["number","null"]},"note":{"type":["string","null"]},"images":{"type":"array","maxItems":5,"items":{"type":"string","format":"uri"},"description":"Up to five https image links. TBBN copies each into its own storage within minutes (resized to fit 1600px) and shows its copy; a link that can't be fetched or isn't an image is dropped. Links uploaded with POST /v1/media/upload-url are used as they are.\n"},"wants":{"type":"array","items":{"$ref":"#/components/schemas/Want"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"syncedAt":{"type":"string","format":"date-time"},"deletedAt":{"type":["string","null"],"format":"date-time"}}},"ListingPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Listing"}},"nextCursor":{"type":["string","null"]},"total":{"type":"integer","description":"Every listing matching","not just this page.":null},"page":{"type":"integer","description":"The page shown, from 1."},"pageCount":{"type":"integer"},"matchedFor":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"}}},"categories":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"count":{"type":"integer"}}}},"subcategories":{"type":"array","items":{"type":"object","properties":{"subcategory":{"type":"string"},"count":{"type":"integer"}}}}}},"SettlementResult":{"type":"object","properties":{"currency":{"type":"string"},"sideAValue":{"type":"number"},"sideBValue":{"type":"number"},"difference":{"type":"number"},"owingSide":{"type":["string","null"],"enum":["A","B",null]}}},"CompatibilityBreakdown":{"type":"object","properties":{"wantsMatch":{"type":"number"},"categoryMatch":{"type":"number"},"brandMatch":{"type":"number"},"conditionMatch":{"type":"number"},"valueMatch":{"type":"number"},"geographyMatch":{"type":"number"},"reputationMatch":{"type":"number"}}},"MatchCandidate":{"type":"object","properties":{"listingId":{"type":"string","format":"uuid"},"merchantId":{"type":"string","format":"uuid"},"title":{"type":"string"},"category":{"type":"string"},"subcategory":{"type":["string","null"]},"brand":{"type":["string","null"]},"condition":{"type":["string","null"]},"originalPrice":{"type":"number"},"requestedAmount":{"type":["number","null"]},"willingToPay":{"type":["number","null"]},"currency":{"type":"string"},"listingType":{"type":"string","enum":["TRADE","BOTH"]},"images":{"type":"array","maxItems":1,"items":{"type":"string","format":"uri"}},"sellerName":{"type":["string","null"]},"merchantDisplayName":{"type":["string","null"]},"merchantLogoUrl":{"type":["string","null"],"format":"uri"},"locationLabel":{"type":["string","null"]},"locationCountry":{"type":["string","null"]},"sellerIdentityVerified":{"type":"boolean"},"score":{"type":"number"},"breakdown":{"$ref":"#/components/schemas/CompatibilityBreakdown"}}},"OfferListing":{"type":"object","properties":{"listing":{"$ref":"#/components/schemas/Listing"}}},"ListingConversationMessage":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"mine":{"type":"boolean"},"body":{"type":["string","null"],"description":"Null while a reported message is hidden for review."},"hidden":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}},"ListingConversationSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["BUYER","SELLER"],"description":"BUYER when you asked about the item; SELLER when it's your item."},"listing":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"image":{"type":["string","null"],"format":"uri"},"status":{"type":"string"}}},"counterpart":{"type":"object","properties":{"name":{"type":["string","null"]}}},"lastMessageAt":{"type":"string","format":"date-time"},"lastMessagePreview":{"type":"string"},"unread":{"type":"boolean"}}},"ListingConversation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["BUYER","SELLER"]},"listing":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"image":{"type":["string","null"],"format":"uri"},"status":{"type":"string"},"listingType":{"type":"string"},"currency":{"type":"string"},"originalPrice":{"type":"number"},"sellerId":{"type":"string","format":"uuid"},"country":{"type":["string","null"],"description":"The country the item was listed in (ISO 3166-1, e.g. NG), when known."}}},"counterpart":{"type":"object","properties":{"name":{"type":["string","null"]}}},"messages":{"type":"array","items":{"$ref":"#/components/schemas/ListingConversationMessage"}}}},"Offer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"fromSellerId":{"type":"string","format":"uuid"},"toSellerId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["DRAFT","PENDING","ACCEPTED","REJECTED","CANCELLED","EXPIRED","COUNTERED"]},"extraAmountOffered":{"type":["number","null"],"description":"Cash the sender adds on top, paid like willing to pay."},"cashIncluded":{"type":["number","null"],"description":"Cash the sender included so the offer is worth at least half the listed price of what it asks for — part of the difference they pay, not on top of it.\n"},"expiresAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"quoteFingerprint":{"type":["string","null"],"description":"The quote the confirmations below are for."},"quoteAcknowledgedFromAt":{"type":["string","null"],"format":"date-time"},"quoteAcknowledgedToAt":{"type":["string","null"],"format":"date-time"},"quote":{"oneOf":[{"$ref":"#/components/schemas/TradeQuote"},{"type":"null"}],"description":"The quote as accepted — set, and locked, when the offer is accepted."},"quoteLockedAt":{"type":["string","null"],"format":"date-time"},"offerListingsA":{"type":"array","items":{"$ref":"#/components/schemas/OfferListing"},"description":"Side A — the existing listing(s) being offered ON, owned by toSellerId.\n"},"offerListingsB":{"type":"array","items":{"$ref":"#/components/schemas/OfferListing"},"description":"Side B — the listing(s) offered IN EXCHANGE, owned by fromSellerId."}}},"CreateOfferInput":{"type":"object","required":["fromSellerId","toSellerId","listingIdsA","listingIdsB","quoteFingerprint"],"properties":{"fromSellerId":{"type":"string","format":"uuid"},"toSellerId":{"type":"string","format":"uuid"},"listingIdsA":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Existing listing(s) being offered on — must be owned by toSellerId (side A per the storage convention)."},"listingIdsB":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Listing(s) offered in exchange — must be owned by fromSellerId (side B). Empty for a request to buy a member's item listed for sale: it's bought at its listed price, paid in person when the two meet (400 NOT_FOR_SALE for an item listed for swaps only, PURCHASE_STORE_ITEM for a store's item).\n"},"extraAmountOffered":{"type":"number","minimum":0,"description":"Cash the sender adds on top, paid like willing to pay."},"cashIncluded":{"type":"number","minimum":0,"description":"Cash included as part of what the sender pays, so the offer is worth at least half the listed price of what it asks for. At most the difference the sender pays.\n"},"expiresAt":{"type":"string","format":"date-time"},"quoteFingerprint":{"type":"string","description":"The fingerprint of the trade quote the sender was shown (POST /v1/offers/quote). Sending the offer confirms that quote; it's refused with 409 QUOTE_CHANGED if the quote has changed since."},"meetupSpaceId":{"type":"string","format":"uuid","description":"Where you'd meet or hand over: a TBBN Space — the one the listing suggests, or another. When the offer is accepted the trade starts with it agreed as the meeting place; it's reserved separately, once both have agreed the deal.\n"},"meetupFromListing":{"type":"boolean","description":"True when meetupSpaceId is the Space the listing suggests."}}},"CounterOfferInput":{"type":"object","required":["actingSellerId","listingIdsA","listingIdsB","quoteFingerprint"],"properties":{"actingSellerId":{"type":"string","format":"uuid"},"listingIdsA":{"type":"array","items":{"type":"string","format":"uuid"}},"listingIdsB":{"type":"array","items":{"type":"string","format":"uuid"}},"extraAmountOffered":{"type":"number","minimum":0},"cashIncluded":{"type":"number","minimum":0,"description":"As for a new offer."},"expiresAt":{"type":"string","format":"date-time"},"quoteFingerprint":{"type":"string","description":"The fingerprint of the counter-offer's quote, as for a new offer."}}},"TradeQuote":{"type":"object","description":"What each trader pays, and at which store, for a proposed trade. Each trader checks out with the store of the items they receive. An item's trade value is its listed price plus any amount asked, minus any amount the seller is willing to pay; cash the sender adds to an offer counts like willing to pay, on side B. The side worth less (the payer) pays the difference to the side worth more (the payee). A store's commission is a percentage of each of its items' listed prices: it comes first out of any difference that store's seller is due, and the rest is charged to the trader checking out. Items listed by an individual have no checkout or commission; a difference due to their seller is paid directly. Tax, card processing fees and shipping are added by the store at checkout.","properties":{"version":{"type":"integer","enum":[1]},"currency":{"type":"string"},"sides":{"type":"object","properties":{"A":{"$ref":"#/components/schemas/TradeQuoteSide"},"B":{"$ref":"#/components/schemas/TradeQuoteSide"}}},"extraCashFromB":{"type":"number","description":"Cash the offer's sender adds on top."},"difference":{"type":"number"},"payer":{"type":["string","null"],"enum":["A","B",null],"description":"Null on an even trade."},"payee":{"type":["string","null"],"enum":["A","B",null]},"checkouts":{"type":"array","items":{"$ref":"#/components/schemas/QuoteCheckout"}},"directPayment":{"type":["object","null"],"description":"A difference owed to an individual seller, paid between the traders directly.","properties":{"fromSide":{"type":"string","enum":["A","B"]},"toSide":{"type":"string","enum":["A","B"]},"amount":{"type":"number"}}},"fingerprint":{"type":"string","description":"Identifies this exact quote. A trader confirms a quote by sending its fingerprint; if any price, amount or store term changes, the fingerprint changes and the quote must be confirmed again."}}},"TradeQuoteSide":{"type":"object","properties":{"listedPrice":{"type":"number"},"tradeValue":{"type":"number"},"items":{"type":"array","items":{"$ref":"#/components/schemas/QuoteItem"}}}},"QuoteItem":{"type":"object","properties":{"listingId":{"type":"string","format":"uuid"},"title":{"type":"string"},"merchantId":{"type":["string","null"],"format":"uuid"},"originalPrice":{"type":"number"},"requestedAmount":{"type":["number","null"]},"willingToPay":{"type":["number","null"]},"tradeValue":{"type":"number"},"commissionRate":{"type":["number","null"]},"commissionDue":{"type":"number"},"cashDue":{"type":"number","description":"This item's share of the difference","when its side is the payee.":null},"offsetFromSeller":{"type":"number"},"commissionShortfall":{"type":"number"},"sellerNetCash":{"type":"number"}}},"QuoteCheckout":{"type":"object","description":"One store's checkout in a trade — selling one side's items to the other side's trader.","properties":{"side":{"type":"string","enum":["A","B"],"description":"The side whose items this checkout sells."},"buyerSide":{"type":"string","enum":["A","B"],"description":"The side whose trader pays here."},"merchantId":{"type":"string","format":"uuid"},"merchantName":{"type":"string"},"commissionRate":{"type":["number","null"]},"listingIds":{"type":"array","items":{"type":"string","format":"uuid"}},"taxBase":{"type":"number","description":"The items' listed prices, for the store's own tax calculation at checkout (how a trade is taxed varies by place)."},"differenceOwed":{"type":"number"},"commissionDue":{"type":"number"},"commissionOffset":{"type":"number","description":"Commission taken from the difference the store's seller is due."},"commissionShortfall":{"type":"number","description":"Commission charged to the trader checking out."},"checkoutFee":{"type":"number","description":"TBBN's checkout fee","when the store passes it on.":null},"processingFeesPassed":{"type":"boolean"},"total":{"type":"number","description":"Difference owed + commission shortfall + checkout fee — before tax, processing fees and shipping, which the store adds."},"sellerNetCash":{"type":"number"},"shipFromRegion":{"type":["string","null"]},"settlement":{"type":"string","enum":["TBBN_CHECKOUT","EXTERNAL"]},"checkoutUrl":{"type":["string","null"],"format":"uri"}}},"QuoteProblem":{"type":"object","description":"A store involved hasn't finished its trade setup, so its items can't be traded yet.","properties":{"code":{"type":"string","enum":["COMMISSION_RATE_MISSING","CHECKOUT_LINK_MISSING"]},"merchantId":{"type":"string","format":"uuid"},"merchantName":{"type":"string"}}},"OfferQuoteView":{"type":"object","properties":{"quote":{"$ref":"#/components/schemas/TradeQuote"},"problems":{"type":"array","items":{"$ref":"#/components/schemas/QuoteProblem"}},"locked":{"type":"boolean","description":"True once the offer is accepted — the quote no longer changes."},"acknowledged":{"type":"object","description":"Whether each party has confirmed the current quote within the last 72 hours.","properties":{"sender":{"type":"boolean"},"recipient":{"type":"boolean"}}}}},"TradeSessionReservation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"tradeSessionId":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["A","B"]},"listingId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING","RESERVED","RELEASED","FAILED"]},"reservedAt":{"type":["string","null"],"format":"date-time"},"releasedAt":{"type":["string","null"],"format":"date-time"}}},"TradeSessionListing":{"type":"object","properties":{"listing":{"$ref":"#/components/schemas/Listing"}}},"TradeSessionPayment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"tradeSessionId":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["A","B"]},"status":{"type":"string","enum":["PENDING","WAITING","COMPLETE","FAILED"]},"merchantCheckoutSessionRef":{"type":["string","null"]},"amount":{"type":"number","description":"What the trader checking out at this side's store owes it, before tax, processing fees and shipping. A side's checkout sells that side's items to the other side's trader."},"breakdown":{"type":["object","null"],"description":"The quote lines behind amount — buyerSide, differenceOwed, commissionDue, commissionOffset, commissionShortfall, checkoutFee, taxBase, sellerNetCash, and each store's lines in stores."},"currency":{"type":"string"},"completedAt":{"type":["string","null"],"format":"date-time"},"failedReason":{"type":["string","null"]},"fulfillmentStartedAt":{"type":["string","null"],"format":"date-time","description":"This side's own fulfillment progress, independent of the other side's. Only settable once this row's own status is COMPLETE."},"fulfillmentCompletedAt":{"type":["string","null"],"format":"date-time"},"settlement":{"type":"string","enum":["TBBN_CHECKOUT","EXTERNAL"],"description":"How this side settles. TBBN_CHECKOUT: TBBN redirects to the merchant's checkout and tracks the payment and fulfillment callbacks. EXTERNAL: the side's merchant runs matching only (checkoutEnabled false) and settles on its own platform, then reports the outcome once with POST /v1/trade-sessions/{id}/external-settlement. Each side is decided by its own merchant; the other side is unaffected."},"externalSettlementDueAt":{"type":["string","null"],"format":"date-time","description":"EXTERNAL sides only — 14 days after checkout opened, plus any extension. A side not reported by then is released as failed."},"externalSettlementExtendedAt":{"type":["string","null"],"format":"date-time"},"externalSettlementExtensionReason":{"type":["string","null"]}}},"TradeMessage":{"type":"object","description":"Trade-scoped chat only (post-launch hardening) — one message, between the two sellers of the named trade session. No standalone conversation resource exists.","properties":{"id":{"type":"string","format":"uuid"},"tradeSessionId":{"type":"string","format":"uuid"},"senderSellerId":{"type":"string","format":"uuid"},"body":{"type":"string"},"status":{"type":"string","enum":["VISIBLE","HIDDEN","REMOVED"]},"createdAt":{"type":"string","format":"date-time"}}},"TradeSession":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"offerId":{"type":["string","null"],"format":"uuid"},"sellerAId":{"type":"string","format":"uuid"},"sellerBId":{"type":"string","format":"uuid"},"status":{"type":"string","description":"See the trade-session state machine guide for the canonical transition table.","enum":["DRAFT","PENDING","OFFERED","ACCEPTED","RESERVING","RESERVED","WAITING_CHECKOUT_A","WAITING_CHECKOUT_B","EXTERNAL_SETTLEMENT","CHECKOUT_A_COMPLETE","CHECKOUT_B_COMPLETE","SCHEDULING_A","SCHEDULING_B","EXCHANGE_A_CONFIRMED","EXCHANGE_B_CONFIRMED","EXCHANGE_FAILED","LOCKED","FULFILLMENT","FULFILLED","COMPLETED","CANCELLED","CLOSED_UNRECIPROCATED","EXPIRED","PAYMENT_FAILED","RESERVATION_FAILED","FULFILLMENT_FAILED","RETURNED","REFUNDED"]},"fulfillmentMode":{"type":"string","enum":["MERCHANT_CHECKOUT","PEER_TO_PEER"],"description":"PEER_TO_PEER is set only when every listing on both sides has no Merchant — SCHEDULING_*/EXCHANGE_*_CONFIRMED/EXCHANGE_FAILED are its states, the counterpart to WAITING_CHECKOUT_*/CHECKOUT_*_COMPLETE on the Merchant path.\n"},"tradeValue":{"type":["number","null"],"description":"The difference between the two sides."},"currency":{"type":"string"},"quote":{"oneOf":[{"$ref":"#/components/schemas/TradeQuote"},{"type":"null"}],"description":"The quote both traders confirmed, locked when the offer was accepted."},"checkouts":{"type":"array","description":"Each store checkout on the trade, from the locked quote, with link (the store's trade checkout link with tbbn_trade_session_id and tbbn_side added), paymentStatus and paidAt.","items":{"allOf":[{"$ref":"#/components/schemas/QuoteCheckout"},{"type":"object","properties":{"link":{"type":["string","null"],"format":"uri"},"paymentStatus":{"type":["string","null"]},"paidAt":{"type":["string","null"],"format":"date-time"}}}]}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time","description":"Pre-payment hold deadline (RESERVED + 7 days). Only meaningful while counterpartyHoldStartedAt is null; the whole session is cancelled automatically if neither side has paid by this time."},"lockedAt":{"type":["string","null"],"format":"date-time","description":"Deprecated, always null — kept for backward compatibility only."},"completedAt":{"type":["string","null"],"format":"date-time"},"counterpartyHoldStartedAt":{"type":["string","null"],"format":"date-time","description":"Set once ONE side completes checkout while the other has not. Starts the unpaid side's minimum hold and weekly reminder emails. Cleared back to null if the other side goes on to pay too."},"lastReminderSentAt":{"type":["string","null"],"format":"date-time"},"closeNote":{"type":["string","null"],"description":"Required note on a manual closeUnreciprocated call; auto-generated for an automatic one."},"listingsA":{"type":"array","items":{"$ref":"#/components/schemas/TradeSessionListing"}},"listingsB":{"type":"array","items":{"$ref":"#/components/schemas/TradeSessionListing"}},"reservations":{"type":"array","items":{"$ref":"#/components/schemas/TradeSessionReservation"}},"payments":{"type":"array","items":{"$ref":"#/components/schemas/TradeSessionPayment"}}}},"BusinessSubscription":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"businessId":{"type":"string","format":"uuid"},"tier":{"type":"string","enum":["SANDBOX","STARTER","GROWTH","ENTERPRISE","PAY_AS_YOU_GO"]},"stripeSubscriptionId":{"type":["string","null"]},"stripeCustomerId":{"type":["string","null"]},"status":{"type":"string"},"currentPeriodStart":{"type":["string","null"],"format":"date-time"},"currentPeriodEnd":{"type":["string","null"],"format":"date-time"},"stripePaymentMethodId":{"type":["string","null"],"description":"Pay-As-You-Go only — the saved card charged every Monday."},"paymentMethodSetupAt":{"type":["string","null"],"format":"date-time"},"lastWeeklyChargeAt":{"type":["string","null"],"format":"date-time","description":"Pay-As-You-Go only — the most recent Monday slot this subscription was billed through."},"waivedUntil":{"type":["string","null"],"format":"date-time","description":"When set, plan payment collection is paused until this time."},"waivedReason":{"type":["string","null"]}}},"UsageLineItem":{"type":"object","properties":{"eventType":{"type":"string","enum":["LISTING_SYNC","SEARCH","OFFER","TRADE_SESSION","WEBHOOK_DELIVERY","CHECKOUT_ORCHESTRATION","STORE_VISIT"]},"rawCount":{"type":"integer"},"meteredUnits":{"type":"number","description":"Raw count with the cross-merchant multiplier applied to CROSS_MERCHANT-scoped events."},"included":{"type":["integer","null"],"description":"Plan-included quota for this event type; null means unlimited/negotiated."},"overageUnits":{"type":"number"},"overageCost":{"type":"number"}}},"UsageSummary":{"type":"object","properties":{"businessId":{"type":"string","format":"uuid"},"merchantIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Every Merchant linked to this Business, whose usage is aggregated below."},"tier":{"type":"string","enum":["SANDBOX","STARTER","GROWTH","ENTERPRISE","PAY_AS_YOU_GO"]},"billingPeriodRef":{"type":"string"},"basePriceUsd":{"type":["number","null"]},"lineItems":{"type":"array","items":{"$ref":"#/components/schemas/UsageLineItem"}},"totalOverageUsd":{"type":"number"},"waived":{"type":"boolean","description":"True while payment collection is paused for this Business. Usage and cost are still shown in full; only amountDueUsd is zero."},"waivedUntil":{"type":["string","null"],"format":"date-time"},"amountDueUsd":{"type":"number","description":"basePriceUsd + totalOverageUsd, or 0 while waived."}}},"Notification":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","description":"The kind of event","e.g. offer.created.":null},"title":{"type":"string","description":"What happened","in the reader's language.":null},"body":{"type":["string","null"]},"href":{"type":["string","null"],"description":"Where it opens."},"readAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}},"InboxThread":{"type":"object","properties":{"kind":{"type":"string","enum":["ITEM","TRADE","SPACE_GUEST","SPACE_HOST"]},"id":{"type":"string","format":"uuid"},"title":{"type":"string","description":"The item","the trade's item":null,"or the Space.":null},"withName":{"type":["string","null"],"description":"Who the conversation is with."},"preview":{"type":"string"},"lastMessageAt":{"type":"string","format":"date-time"},"unread":{"type":"integer","description":"Messages from the other side not yet read."},"href":{"type":"string"}}},"AdminUserSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"status":{"type":"string","enum":["ACTIVE","SUSPENDED","DELETED"]},"authType":{"type":"string","enum":["MARKETPLACE_MEMBER","MERCHANT_REP"]},"country":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}}},"AdminUserDetail":{"type":"object","properties":{"account":{"allOf":[{"$ref":"#/components/schemas/AdminUserSummary"},{"type":"object","properties":{"identityStatus":{"type":"string","enum":["UNVERIFIED","PENDING","VERIFIED","NEEDS_ATTENTION"]},"allNonEssentialEmailOptOut":{"type":"boolean"}}}]},"seller":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]},"reputationScore":{"type":["integer","null"]},"listingCount":{"type":"integer"},"offerCount":{"type":"integer"},"tradeSessionCount":{"type":"integer"}}},"merchantOrgs":{"type":"array","items":{"type":"object","properties":{"merchantId":{"type":"string","format":"uuid"},"merchantName":{"type":"string"},"role":{"type":"string"}}}},"activeSessionCount":{"type":"integer"}}},"WebhookSubscription":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"merchantId":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true,"description":"Your own name for the endpoint."},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"},"description":"Event type strings (e.g. offer.accepted) or [\"*\"] for every event."},"secret":{"type":"string","description":"HMAC signing secret for the x-tbbn-signature delivery header. Shown at creation time."},"status":{"type":"string","enum":["ACTIVE","DISABLED"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","nullable":true}}},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"subscriptionId":{"type":"string","format":"uuid"},"eventType":{"type":"string"},"payload":{"type":"object"},"status":{"type":"string","enum":["PENDING","DELIVERED","FAILED","DEAD_LETTER"]},"attempts":{"type":"integer"},"lastAttemptedAt":{"type":["string","null"],"format":"date-time"},"nextAttemptAt":{"type":["string","null"],"format":"date-time","description":"When a FAILED delivery is retried next; null once delivered or out of retries."},"createdAt":{"type":"string","format":"date-time"}}},"AuditLog":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"actorType":{"type":"string","enum":["MERCHANT_REP","MARKETPLACE_MEMBER","TBBN_OPS","SYSTEM"]},"actorId":{"type":["string","null"]},"action":{"type":"string"},"targetType":{"type":["string","null"]},"targetId":{"type":["string","null"]},"metadata":{"type":"object"},"occurredAt":{"type":"string","format":"date-time"}}},"ModerationFlag":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"merchantId":{"type":"string","format":"uuid"},"sellerId":{"type":["string","null"],"format":"uuid"},"listingId":{"type":["string","null"],"format":"uuid"},"fieldName":{"type":"string"},"matchedPattern":{"type":"string"},"rawExcerpt":{"type":"string"},"status":{"type":"string","enum":["FLAGGED","OVERRIDDEN","UPHELD"]},"reviewedBy":{"type":["string","null"]},"reviewedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}},"FraudSignal":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sellerId":{"type":"string","format":"uuid"},"signalType":{"type":"string"},"severity":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"metadata":{"type":"object"},"detectedAt":{"type":"string","format":"date-time"}}},"FeatureFlag":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"key":{"type":"string"},"merchantId":{"type":["string","null"],"format":"uuid","description":"null means a global default; set means a per-merchant override."},"enabled":{"type":"boolean"},"description":{"type":["string","null"]},"updatedAt":{"type":"string","format":"date-time"}}}},"responses":{"ErrorResponse":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/auth/merchant/otp/request":{"post":{"tags":["Auth"],"summary":"Request email OTP for merchant rep login","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}}}}},"responses":{"202":{"description":"OTP sent"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/merchant/otp/verify":{"post":{"tags":["Auth"],"summary":"Verify merchant rep OTP and issue a session","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","code"],"properties":{"email":{"type":"string"},"code":{"type":"string"},"merchantId":{"type":"string","format":"uuid","description":"Sign in to this Merchant. Without it, the one the person owns, else the one they joined first.\n"}}}}}},"responses":{"200":{"description":"Session issued, bound to the Merchant","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"merchantId":{"type":"string","format":"uuid"},"role":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/merchant/switch":{"post":{"tags":["Auth"],"summary":"Move a merchant session to another Merchant the person is on","description":"For someone on several Merchants' teams — their own and their employers'. The refresh token is exchanged for one bound to the chosen Merchant, with the person's role there.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["refreshToken","merchantId"],"properties":{"refreshToken":{"type":"string"},"merchantId":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"New session on that Merchant","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"merchantId":{"type":"string","format":"uuid"},"role":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/merchant/access":{"post":{"security":[{"BearerAuth":[]}],"tags":["Auth"],"summary":"Open a Merchant from your TBBN sign-in","description":"Signed in on tbbnetwork.com, returns an access token for a Merchant you're on — the one you name, otherwise the one you own or joined first. No new sign-in and no new refresh token: when the access token runs out, ask again. Answers 403 with code `NO_MERCHANT` when you aren't on any Merchant's team, and `NOT_ON_MERCHANT_TEAM` for a Merchant you aren't on.\n","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"merchantId":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"An access token on that Merchant","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"merchantId":{"type":"string","format":"uuid"},"role":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/account/workplaces":{"get":{"security":[{"BearerAuth":[]}],"tags":["Auth"],"summary":"The Businesses and Merchants you own or work for","description":"With your role at each, whether a Business hosts Spaces, and how many team invitations are waiting for you.\n","responses":{"200":{"description":"Workplaces","content":{"application/json":{"schema":{"type":"object","properties":{"businesses":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"logoUrl":{"type":["string","null"]},"role":{"type":"string"},"hostsSpaces":{"type":"boolean"}}}},"merchants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"logoUrl":{"type":["string","null"]},"role":{"type":"string"},"status":{"type":"string"}}}},"pendingInvitations":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/member/register":{"post":{"tags":["Auth"],"summary":"Direct self-registration for a Marketplace profile — the primary way anyone joins TBBN now.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"country":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country of residence. Optional; asked again at identity verification if missing."}}}}}},"responses":{"202":{"description":"OTP sent"},"409":{"description":"An account with this email or phone already exists"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/member/otp/verify":{"post":{"tags":["Auth"],"summary":"Complete self-registration and issue a session","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["target","code"],"properties":{"target":{"type":"string"},"code":{"type":"string"}}}}}},"responses":{"200":{"description":"Session issued (User + Seller created together)","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/seller/otp/request":{"post":{"tags":["Auth"],"summary":"Request OTP for a marketplace member's direct TBBN Marketplace login","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}}}}},"responses":{"202":{"description":"OTP sent"},"404":{"description":"No marketplace account found for this email"}}}},"/v1/auth/seller/otp/verify":{"post":{"tags":["Auth"],"summary":"Verify marketplace member OTP and issue a session","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","code"],"properties":{"email":{"type":"string"},"code":{"type":"string"}}}}}},"responses":{"200":{"description":"Session issued","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/seller/magic-link/request":{"post":{"tags":["Auth"],"summary":"Request a passwordless magic link for a marketplace member","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}}}}},"responses":{"202":{"description":"Magic link sent"}}}},"/v1/auth/seller/magic-link/consume":{"get":{"tags":["Auth"],"summary":"Consume a magic link token, issuing a session","security":[],"parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Session issued","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"}}}}}}}}},"/v1/auth/refresh":{"post":{"tags":["Auth"],"summary":"Rotate a refresh token for a new access token","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["refreshToken"],"properties":{"refreshToken":{"type":"string"}}}}}},"responses":{"200":{"description":"New token pair (old refresh token invalidated)","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"}}}}}},"401":{"description":"Refresh token invalid, expired, or reused (triggers session-chain revocation)"}}}},"/v1/auth/logout":{"post":{"tags":["Auth"],"summary":"Revoke the current session","responses":{"204":{"description":"Logged out"}}}},"/v1/auth/me":{"get":{"tags":["Auth"],"summary":"Who the credential belongs to. With an API key: the Merchant it's for and whether it's a test key (livemode false) — a quick way to check a key works. With a sign-in token: the signed-in account.\n","responses":{"200":{"description":"Current principal","content":{"application/json":{"schema":{"type":"object","properties":{"principalType":{"type":"string","enum":["merchant_api_key","space_api_key","merchant_rep","marketplace_member"]},"userId":{"type":"string","format":"uuid"},"merchantId":{"type":["string","null"],"format":"uuid"},"merchant":{"type":["object","null"],"description":"API keys only — the Merchant the key belongs to.","properties":{"id":{"type":"string","format":"uuid"},"displayName":{"type":"string"},"status":{"type":"string"},"livemode":{"type":"boolean"}}},"businessId":{"type":["string","null"],"format":"uuid","description":"Space API keys only."},"livemode":{"type":"boolean","description":"API keys only — false for a test key (sk_sandbox_, sk_space_sandbox_)."},"restriction":{"type":["string","null"],"enum":["WIND_DOWN",null],"description":"API keys only — WIND_DOWN while the Merchant's production access is paused; existing trades can still be finished.\n"},"sellerId":{"type":["string","null"],"format":"uuid"},"role":{"type":["string","null"]}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/step-up/request":{"post":{"security":[{"BearerAuth":[]}],"tags":["Auth"],"summary":"Send a fresh verification code to the signed-in caller's own account email. Sensitive actions (account deletion, editing an already-verified Business, Merchant or Space) need this code confirmed first, on top of the session.\n","responses":{"202":{"description":"Verification code sent"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/step-up/confirm":{"post":{"security":[{"BearerAuth":[]}],"tags":["Auth"],"summary":"Verify the step-up code and receive a short-lived (5 minute), single-action token. Pass it back as X-TBBN-Step-Up-Token on the one request it was scoped for — a step-up token is never a substitute for that route's own principal/role check, only an added re-authentication layer on top of it.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code","scope"],"properties":{"code":{"type":"string"},"scope":{"type":"string","description":"The one action this token unlocks, e.g. \"account:delete\", \"business:update\", \"merchant:update\", \"space:update\".\n"}}}}}},"responses":{"200":{"description":"Step-up token","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/account/export":{"get":{"tags":["Auth"],"security":[{"BearerAuth":[]}],"summary":"Download everything TBBN holds about the signed-in member, as JSON","description":"One section per kind of data (account, marketplace profile, listings, trades and messages, Space bookings, memberships, invoices, reviews, reports, Organizations, blog activity and more), plus a guide saying what each section holds and what deleting the account does to it.\n","responses":{"200":{"description":"Account data export","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"exportedAt":{"type":"string","format":"date-time"},"guide":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"holds":{"type":"string"},"onDelete":{"type":"string","enum":["ERASE","ANONYMIZE","TAKE_DOWN","KEEP"]},"why":{"type":"string"}}}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/account/delete":{"post":{"tags":["Auth"],"security":[{"BearerAuth":[]}],"summary":"Delete the signed-in member's account","description":"Removes the member's name and contact details, messages, comments and saved items, takes down their own listings and signs out every device. Trade, booking and payment records stay for the other people involved and for tax law, no longer linked to the member. Refused (409 ACCOUNT_HAS_ACTIVITY) while a trade is in progress or there's an upcoming Space booking or active membership. Needs a step-up token scoped to \"account:delete\".\n","parameters":[{"name":"X-TBBN-Step-Up-Token","in":"header","required":true,"schema":{"type":"string"},"description":"From POST /v1/auth/step-up/confirm, scope \"account:delete\"."}],"responses":{"204":{"description":"Account deleted"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/account/avatar":{"put":{"security":[{"BearerAuth":[]}],"tags":["Auth"],"summary":"Set the signed-in member's profile photo","description":"An https image URL, uploaded first with POST /v1/media/upload-url (folder avatars). Shown on the member's own account menu.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["avatarUrl"],"properties":{"avatarUrl":{"type":"string","format":"uri"}}}}}},"responses":{"200":{"description":"Saved"},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"delete":{"security":[{"BearerAuth":[]}],"tags":["Auth"],"summary":"Remove the signed-in member's profile photo","responses":{"200":{"description":"Removed"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/account/location":{"put":{"security":[{"BearerAuth":[]}],"tags":["Auth"],"summary":"Save the signed-in member's approximate location","description":"Stored rounded to about a kilometre and used only for the distance limit some listings have. Other members never see it.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["latitude","longitude"],"properties":{"latitude":{"type":"number","minimum":-90,"maximum":90},"longitude":{"type":"number","minimum":-180,"maximum":180}}}}}},"responses":{"200":{"description":"Saved"},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"delete":{"security":[{"BearerAuth":[]}],"tags":["Auth"],"summary":"Clear the signed-in member's saved location","responses":{"200":{"description":"Cleared"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/account/work-email/providers":{"get":{"tags":["Auth"],"security":[{"BearerAuth":[]}],"summary":"Which school or work account sign-ins can verify an email (google, microsoft).","responses":{"200":{"description":"The providers offered","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"string","enum":["google","microsoft"]}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/account/work-email/start":{"post":{"tags":["Auth"],"security":[{"BearerAuth":[]}],"summary":"Starts verifying a school or work email by signing in to that account. Send the member to url; they come back to tbbnetwork.com/account/work-email with code and state.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["provider"],"properties":{"provider":{"type":"string","enum":["google","microsoft"]}}}}}},"responses":{"200":{"description":"Where to sign in","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/auth/account/work-email/finish":{"post":{"tags":["Auth"],"security":[{"BearerAuth":[]}],"summary":"Finishes the sign-in as the same member who started it and adds the address to their account as verified. Only an address whose domain the organization owns is accepted.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code","state"],"properties":{"code":{"type":"string","maxLength":2048},"state":{"type":"string","maxLength":2048}}}}}},"responses":{"200":{"description":"The verified address","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/businesses":{"post":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Create a Business, owned by the authenticated Marketplace member","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessInput"}}}},"responses":{"201":{"description":"Business created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Business"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"List the authenticated Marketplace member's own Businesses","parameters":[{"name":"ownerUserId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Businesses","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Business"}}}}}}}},"/v1/businesses/{id}":{"get":{"tags":["Businesses"],"summary":"Get a Business","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Business","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Business"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"patch":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Update a Business (owner or Admin). Once verificationStatus is VERIFIED, changing who it legally is (name, legal name, registration number, tax id, legal country) also requires a step-up token scoped to \"business:update\" — see POST /v1/auth/step-up/confirm. Brand and contact details (logoUrl, websiteUrl, supportEmail, supportPhone, privacyPolicyUrl, termsUrl — https links; an empty string clears one) never need one; the Business's Merchants and Spaces show them unless they set their own.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"X-TBBN-Step-Up-Token","in":"header","required":false,"schema":{"type":"string"},"description":"Required only to change legal details once this Business is VERIFIED."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Every field optional; send only what changes.","properties":{"name":{"type":"string"},"description":{"type":"string"},"legalName":{"type":"string"},"registrationNumber":{"type":"string"},"taxId":{"type":"string"},"legalCountry":{"type":"string"},"logoUrl":{"type":"string"},"websiteUrl":{"type":"string"},"supportEmail":{"type":"string"},"supportPhone":{"type":"string"},"privacyPolicyUrl":{"type":"string"},"termsUrl":{"type":"string"},"openingHours":{"$ref":"#/components/schemas/OpeningHours"}}}}}},"responses":{"200":{"description":"Business updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Business"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"delete":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Soft-delete a Business (owner only)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Business marked DELETED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Business"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/businesses/{businessId}/business-users":{"post":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Invite someone to the team with a role (owner or Admin)","description":"Sends an invitation to an email address — the person needn't have a TBBN account yet — or to a TBBN Account ID. They're emailed, and join with the role when they accept. Optionally limit them to some locations with `branchIds`; they then see and act on only those locations. The owner can't be invited, and the owner role can't be given. Inviting the same person again replaces their open invitation.\n","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["role"],"description":"Give email or userId.","properties":{"email":{"type":"string","format":"email"},"userId":{"type":"string","format":"uuid","description":"A TBBN Account ID."},"role":{"type":"string","enum":["ADMIN","ACCOUNT_MANAGER","OPERATIONS","ACCOUNTANT","DEVELOPER","SUPPORT","RECEPTION","VIEWER"]},"branchIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Locations of this Business to limit the person to. Omit for all."}}}}}},"responses":{"201":{"description":"Invitation sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamInvitation"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"List a Business's team (owner or Admin)","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Team members","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BusinessUser"}}}}}}}},"/v1/businesses/{businessId}/invitations":{"get":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"The team's invitations waiting for an answer (owner or Admin)","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Invitations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TeamInvitation"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/businesses/{businessId}/invitations/{invitationId}":{"delete":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Withdraw an invitation (owner or Admin)","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"invitationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Withdrawn","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamInvitation"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/businesses/invitations/mine":{"get":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Invitations to Business teams waiting for you","responses":{"200":{"description":"Your invitations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MyTeamInvitation"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/businesses/invitations/{invitationId}/accept":{"post":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Accept an invitation and join the team with its role","description":"Signed in with the invited email (or the invited account).","parameters":[{"name":"invitationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Joined","content":{"application/json":{"schema":{"type":"object","properties":{"businessId":{"type":"string","format":"uuid"},"businessName":{"type":"string"},"role":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/businesses/invitations/{invitationId}/decline":{"post":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Decline an invitation","parameters":[{"name":"invitationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Declined"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/businesses/{businessId}/business-users/{userId}":{"patch":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Change a team member's role or locations (owner or Admin)","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["ADMIN","ACCOUNT_MANAGER","OPERATIONS","ACCOUNTANT","DEVELOPER","SUPPORT","RECEPTION","VIEWER"]},"branchIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The locations to limit the person to. An empty list means all."}}}}}},"responses":{"200":{"description":"Role updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessUser"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"delete":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Remove a team member (owner or Admin; the owner can't be removed)","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Removed"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/businesses/{businessId}/branches":{"post":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Add a Branch (location) to a Business (owner only)","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BranchInput"}}}},"responses":{"201":{"description":"Branch created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Branch"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"tags":["Businesses"],"summary":"List a Business's Branches","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Branches","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Branch"}}}}}}}},"/v1/branches/{id}":{"patch":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Update a Branch (owner only)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BranchInput"}}}},"responses":{"200":{"description":"Branch updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Branch"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"delete":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Remove a Branch (owner only)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Branch removed"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/branches/{id}/space-status":{"post":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Toggle a Branch's Space eligibility flag (owner only). Hook only — no Space product logic exists yet.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["spaceStatus"],"properties":{"spaceStatus":{"type":"string","enum":["NOT_ENABLED","SPACE_ENABLED"]}}}}}},"responses":{"200":{"description":"Branch updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Branch"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/businesses/{businessId}/merchant-links":{"post":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Link a Merchant account to a Business (owner only, many-to-many)","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["merchantId"],"properties":{"merchantId":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Link created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessMerchantLink"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"List a Business's linked Merchant accounts","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Links","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BusinessMerchantLink"}}}}}}}},"/v1/businesses/{businessId}/merchant-links/{linkId}/revoke":{"post":{"security":[{"BearerAuth":[]}],"tags":["Businesses"],"summary":"Revoke a Business<->Merchant link (owner only)","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"linkId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Link revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessMerchantLink"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/business-merchant-links":{"get":{"tags":["Businesses"],"summary":"The Businesses a Merchant is linked to. Requires that Merchant's own credential.\n","parameters":[{"name":"merchantId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Links","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BusinessMerchantLink"}}}}}}}},"/v1/space/branches/{branchId}/spaces":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Host creates a Space (a bookable room within this Branch) — requires a team role that manages Spaces at this location (owner, Admin, Account manager or Operations). Verification is not required; an unverified Business can create a Space exactly like a verified one (it just shows an \"Unverified\" badge publicly). Verification instead gates Merchant creation, a separate resource.\n","parameters":[{"name":"branchId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","hoursSource","bookingLengthValue","bookingLengthUnit"],"description":"A Space needs opening hours (its Business's, or its own) and a booking length; otherwise it's refused with 400 SPACE_SETUP_INCOMPLETE listing what's missing.","properties":{"name":{"type":"string"},"description":{"type":"string"},"hoursSource":{"type":["string","null"],"enum":["BUSINESS","CUSTOM",null],"description":"Where the Space's opening hours come from — its Business's hours (BUSINESS) or its own (CUSTOM, in openingHours). Required to finish setting up a Space."},"openingHours":{"$ref":"#/components/schemas/OpeningHours"},"bookingLengthValue":{"type":["integer","null"],"description":"How long each booking lasts, in bookingLengthUnit. Required to finish setup."},"bookingLengthUnit":{"type":["string","null"],"enum":["MINUTES","HOURS","DAYS","WEEKS","MONTHS",null],"description":"At most 1440 minutes, 24 hours, 31 days, 52 weeks or 12 months (calendar months — a year at most). Each booking's end is its start plus this length."},"spotLabel":{"type":["string","null"],"description":"What each spot is called (\"Table\", \"Desk\", \"Room\"). Null means \"Spot\"."},"category":{"type":"string","enum":["STUDY_ROOM","MEETING_ROOM","OFFICE_ROOM","EVENT_SPACE","RESTAURANT_TABLE","RETAIL_CORNER","PARKING_SPOT","COWORK","OTHER"]},"checkInMethod":{"type":"string","enum":["SELF_SERVICE_NUMBERED","SIGNAGE","STAFF_ASSISTED","OTHER"]},"parkingAvailable":{"type":"boolean"},"parkingNotes":{"type":"string"},"weeklyHours":{"type":"array","items":{"type":"object"}},"maxConcurrentBookings":{"type":"integer"},"defaultBookingDurationMinutes":{"type":"integer"},"photos":{"type":"array","items":{}},"notes":{"type":"string"},"visibility":{"type":"string","enum":["PUBLIC","API_ONLY","RESTRICTED_EMAIL"]},"allowedEmails":{"type":"array","items":{"type":"string"}},"allowedEmailDomains":{"type":"array","items":{"type":"string"}},"isFree":{"type":"boolean"},"capacity":{"type":"integer","description":"Seats — WORKSPACE bookings and listing display."},"amenities":{"type":"array","items":{"type":"string"}},"cancellationPolicy":{"type":"string","enum":["FLEXIBLE","MODERATE","STRICT"]},"wifiInstructions":{"type":"string","description":"Shown to a booker only once paid — never in a public listing."},"blackoutDates":{"type":"array","items":{"type":"object"},"description":"{ start: \"YYYY-MM-DD\", end: \"YYYY-MM-DD\", label?: string }[]"},"bufferMinutes":{"type":"integer"},"minLeadTimeMinutes":{"type":"integer"},"maxAdvanceDays":{"type":["integer","null"],"minimum":1,"maximum":730,"description":"How far ahead a booking may start; null for no limit."},"externalCalendarUrl":{"type":"string","description":"A host's own calendar's ICS export URL, polled for busy windows two-way alongside this Space's own icalToken export."}}}}}},"responses":{"201":{"description":"Space created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Space"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"tags":["Space"],"summary":"List a Branch's Spaces. Returns the full Space shape (including icalToken/ externalCalendarUrl/wifiInstructions/allowedEmails/allowedEmailDomains) only to that Business's team members who can see this location's bookings; every other caller — including unauthenticated — gets SpacePublicView instead, which never includes those fields.\n","parameters":[{"name":"branchId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Spaces — an array of Space (owner) or SpacePublicView (everyone else), never a mix of both within one response.","content":{"application/json":{"schema":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/SpacePublicView"},{"$ref":"#/components/schemas/Space"}]}}}}}}}},"/v1/space/spaces/{id}":{"get":{"tags":["Space"],"summary":"Get a Space. Returns the full Space shape (including icalToken/externalCalendarUrl/ wifiInstructions/allowedEmails/allowedEmailDomains) only to the owning Business's team members who can see this location's bookings; every other caller — including unauthenticated — gets SpacePublicView instead, which never includes those fields. 404s for a non-owner if the Space isn't currently visible to them at all (DRAFT/DISABLED, or a visibility restriction they don't satisfy), rather than confirming the id exists.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Space (owner) or SpacePublicView (everyone else).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SpacePublicView"},{"$ref":"#/components/schemas/Space"}]}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"patch":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Update a Space's profile/visibility (owner-role only). Once the owning Business's verificationStatus is VERIFIED, also requires a step-up token scoped to \"space:update\" — see POST /v1/auth/step-up/confirm.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"X-TBBN-Step-Up-Token","in":"header","required":false,"schema":{"type":"string"},"description":"Required only once the owning Business is VERIFIED."}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","description":"Accepts any settable Space field. Every update must leave the Space with opening hours and a booking length (hoursSource, openingHours, bookingLengthValue, bookingLengthUnit — see Space), except one that only pauses it ({\"status\": \"DISABLED\"}); otherwise 400 SPACE_SETUP_INCOMPLETE.","properties":{"hoursSource":{"type":["string","null"],"enum":["BUSINESS","CUSTOM",null],"description":"Where the Space's opening hours come from — its Business's hours (BUSINESS) or its own (CUSTOM, in openingHours). Required to finish setting up a Space."},"openingHours":{"oneOf":[{"$ref":"#/components/schemas/OpeningHours"},{"type":"null"}]},"bookingLengthValue":{"type":["integer","null"],"description":"How long each booking lasts, in bookingLengthUnit. Required to finish setup."},"bookingLengthUnit":{"type":["string","null"],"enum":["MINUTES","HOURS","DAYS","WEEKS","MONTHS",null],"description":"At most 1440 minutes, 24 hours, 31 days, 52 weeks or 12 months (calendar months — a year at most). Each booking's end is its start plus this length."},"spotLabel":{"type":["string","null"],"description":"What each spot is called (\"Table\", \"Desk\", \"Room\"). Null means \"Spot\"."},"maxConcurrentBookings":{"type":"integer","minimum":1,"description":"Spots."},"hostAcceptsLiability":{"type":"boolean"},"liabilityCapAmount":{"type":["number","null"]}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Space"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"delete":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Delete a Space (owner-role only)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/spaces/{spaceId}/pricing":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Host sets per-service-type pricing for a Space (owner role only)","parameters":[{"name":"spaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["serviceType","feeAmount"],"properties":{"serviceType":{"type":"string","enum":["MEETUP","DROPOFF_PICKUP","WORKSPACE"]},"feeAmount":{"type":"number","description":"In the location's currency — its country's where TBBN is live there, USD otherwise.\n"},"feeCurrency":{"type":"string","deprecated":true,"description":"Ignored. The location's currency is used."},"maxHoldDays":{"type":"integer"},"perDayFee":{"type":"number"},"pricingUnit":{"type":"string","enum":["PER_BOOKING","PER_HOUR","PER_DAY"]},"minDurationMinutes":{"type":"integer"},"maxDurationMinutes":{"type":"integer"},"requiresApproval":{"type":"boolean","description":"WORKSPACE only. When true, a direct booking goes to AWAITING_APPROVAL instead of straight to PENDING_PAYMENT."}}}}}},"responses":{"201":{"description":"Pricing set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceServicePricing"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"security":[],"tags":["Space"],"summary":"List a Space's pricing","parameters":[{"name":"spaceId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Pricing","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SpaceServicePricing"}}}}}}}},"/v1/space/businesses/{businessId}/connect-account":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Business opens its Stripe Connect payout account (ADMIN role or owner). One account per Business, shared by every Branch it runs.","description":"Returns a Stripe link: onboarding to start or finish the Business's account, or, once it's finished (`dashboard: true`), the account's Stripe dashboard to change payout details. Calling it again always opens the same account. `email` defaults to your account email.\n","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"}}}}}},"responses":{"201":{"description":"A Stripe link for the Business's account","content":{"application/json":{"schema":{"type":"object","properties":{"onboardingUrl":{"type":"string","format":"uri"},"dashboard":{"type":"boolean"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings":{"post":{"tags":["Space"],"summary":"Create a Space booking. With a member session, books and starts payment. With a Space API key, books only the key's own Business's Spaces (another Business's Space is 404): a key in INTERNAL_FREE mode (the default) makes a free, confirmed internal reservation and returns bookingUrl; a key in CHARGE_BOOKER mode returns paymentUrl or consentUrl. A test key (sk_space_sandbox_) makes a test booking (livemode false): never charged, never holding the Space, nobody notified, and its link opens a test page where consent and payment are simulated.\nA Space with opening hours and a booking length is booked in slots: scheduledAt must be the start of an AVAILABLE slot from GET /v1/space/spaces/{id}/availability. Otherwise the booking is refused with 400 and a code saying why — NOT_A_BOOKING_TIME, SLOT_FULL, SLOT_BLOCKED, SLOT_TOO_SOON, SLOT_TOO_FAR or SLOT_PAST — a message naming the next open times, and details.nextAvailable, the next few slot starts.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["spaceId","serviceType"],"properties":{"spaceId":{"type":"string","format":"uuid","description":"The Space to book."},"serviceType":{"type":"string","enum":["MEETUP","DROPOFF_PICKUP","WORKSPACE"]},"scheduledAt":{"type":"string","format":"date-time","description":"MEETUP, WORKSPACE. Required for WORKSPACE, and for a MEETUP booked with a member session (400 MEETUP_TIME_REQUIRED); optional for a MEETUP booked with a Space API key."},"durationMinutes":{"type":"integer","description":"WORKSPACE only, required."},"holdDays":{"type":"integer"},"tradeSessionId":{"type":"string","format":"uuid","description":"Nullable forward-compat hook."},"externalClientRef":{"type":"string","description":"With a Space API key, required: your own identifier for who the booking is for."},"requireConsent":{"type":"boolean","description":"With a Space API key set to CHARGE_BOOKER — overrides the key's consent setting.\n"},"guestName":{"type":"string","description":"WORKSPACE only — visitor sign-in details, optional."},"guestCompany":{"type":"string"},"recipientEmail":{"type":"string","format":"email","description":"DROPOFF_PICKUP only — the email on the TBBN account of whoever picks the item up, if it isn't the person booking (422 RECIPIENT_ACCOUNT_REQUIRED if there's no account). They accept the drop-off and get their pickup PIN in that account; the item can't be dropped off until they accept. Leave it out to collect the item yourself.\n"},"recipientName":{"type":"string","description":"DROPOFF_PICKUP only."}}}}}},"responses":{"201":{"description":"Booking created. With a Space API key the response is { booking, bookingUrl } (INTERNAL_FREE) or { booking, paymentUrl | consentUrl } (CHARGE_BOOKER).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceBooking"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"List bookings for a branch or Space (host calendar — any team member of the owning Business who can see that location), for a trader (only their own), or for a headless-booking Business (any team role). With a Space API key: bookings at the key's own Spaces, filtered by spaceId, branchId, from and to. Host and key views never include PINs, check-in codes or link tokens.\n","parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"branchId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"spaceId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"bookedByUserId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"bookedByBusinessId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Bookings","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SpaceBooking"}}}}}}}},"/v1/space/bookings/{id}/consent":{"post":{"security":[],"tags":["Space"],"summary":"Public, token-gated — accepts a headless booking's required consent step, moving it from AWAITING_CONSENT to PENDING_PAYMENT and returning a paymentUrl.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string"}}}}}},"responses":{"201":{"description":"Consent accepted","content":{"application/json":{"schema":{"type":"object","properties":{"paymentUrl":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/confirm":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Either the trader or the host confirms the exchange happened — this is what makes the payout eligible to go out (nothing pays out at checkout/payment-clearing time). The payout itself is created here but disbursed in the next weekly batch (Mondays, 08:00 America/Los_Angeles), not at the moment this call completes.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Booking completed, payout created","content":{"application/json":{"schema":{"type":"object","properties":{"booking":{"$ref":"#/components/schemas/SpaceBooking"},"payout":{"$ref":"#/components/schemas/SpacePayout"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/cancel":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Trader cancels (or a Space API key cancels a booking it made). An internal reservation, or a booking that hasn't been paid, is simply cancelled. A paid booking is refunded in full if it's within the Space's cancellationPolicy window; outside it, the booking still cancels but is not refunded, and the host is paid as if it had been confirmed.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Booking cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceBooking"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/verify-check-in":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Host/reception enters the check-in code a Coworking space guest showed up with.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["pin"],"properties":{"pin":{"type":"string"}}}}}},"responses":{"201":{"description":"Checked in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceBooking"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/verify-dropoff-pin":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Host front desk, drop-off bookings. Checks the PIN the person dropping off gives in person. Refused (409) until that person has added a photo of the item through the handover; the host takes no photo. Every party is emailed.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["pin"],"properties":{"pin":{"type":"string"},"photoUrl":{"type":"string","deprecated":true,"description":"Ignored."}}}}}},"responses":{"201":{"description":"Drop-off verified","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceBooking"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/verify-pickup-pin":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Host front desk, drop-off bookings. Checks the PIN the person picking up gives in person, once they have added a photo of the item, and completes the booking. Every party is emailed.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["pin"],"properties":{"pin":{"type":"string"},"photoUrl":{"type":"string","deprecated":true,"description":"Ignored."}}}}}},"responses":{"201":{"description":"Pickup verified","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceBooking"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/handover":{"get":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"A drop-off booking's handover — status, photos, timeline","description":"For the three parties: the person dropping off (their session or the booking's payment link token), the person picking up (signed in to the TBBN account the booking names) and the host's team. Each sees only their own PIN; the pickup PIN appears once the item has been dropped off. Anyone else gets 404.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"token","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Handover","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceHandover"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/handover/photos":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Add a drop-off or pickup photo of the item","description":"DROPOFF photos come from the person dropping off, before the host accepts the item. PICKUP photos come from the person picking up (or the person who dropped it off, when they're collecting it themselves), after drop-off and before pickup. Up to 5 each. Upload the image first with POST /v1/media/upload-url (folder bookings).\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["stage","photoUrl"],"properties":{"stage":{"type":"string","enum":["DROPOFF","PICKUP"]},"photoUrl":{"type":"string","format":"uri"},"token":{"type":"string","description":"The token from the booking's payment link."}}}}}},"responses":{"201":{"description":"Photo added; the updated handover","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceHandover"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/pickups":{"get":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Drop-offs the signed-in member is picking up","responses":{"200":{"description":"Pickups, newest first","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"bookingId":{"type":"string","format":"uuid"},"status":{"type":"string"},"bookedByName":{"type":["string","null"]},"spaceName":{"type":"string"},"locationName":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"holdUntil":{"type":["string","null"],"format":"date-time"},"droppedOffAt":{"type":["string","null"],"format":"date-time"},"pickedUpAt":{"type":["string","null"],"format":"date-time"},"recipientAccepted":{"type":["boolean","null"]},"needsAnswer":{"type":"boolean"}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/recipient/respond":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Accept or decline picking up a drop-off","description":"For the person picking up, signed in. The item can't be dropped off until they accept. They can change their answer until it's dropped off.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["accept"],"properties":{"accept":{"type":"boolean"}}}}}},"responses":{"201":{"description":"The updated handover","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceHandover"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/recipient":{"put":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Change who picks up a drop-off","description":"For the person who booked, until the item is dropped off. Name the person by the email on their TBBN account (422 RECIPIENT_ACCOUNT_REQUIRED if there isn't one); they're asked to accept. Send no email to collect it yourself.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"recipientEmail":{"type":"string","format":"email"},"recipientName":{"type":"string"},"token":{"type":"string","description":"The token from the booking's payment link."}}}}}},"responses":{"200":{"description":"The updated handover","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceHandover"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/test-bookings":{"get":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"A Business's test-mode bookings","description":"What the Business's test Space API keys (sk_space_sandbox_) booked, newest first, each with the link to its test page. For the Owner, Admins and Developers.\n","parameters":[{"name":"businessId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Test bookings","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SpaceTestBooking"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/test-bookings/{id}":{"get":{"security":[],"tags":["Space"],"summary":"A test booking, for its hosted test page","description":"Authorized by the token in the link a test key's booking returned.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"token","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The test booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceTestBooking"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/test-bookings/{id}/simulate":{"post":{"security":[],"tags":["Space"],"summary":"Simulate the booker giving consent, paying, or the payment failing","description":"consent moves AWAITING_CONSENT to PENDING_PAYMENT; pay moves PENDING_PAYMENT to PAID; fail cancels it. No money moves and nobody is notified.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token","action"],"properties":{"token":{"type":"string"},"action":{"type":"string","enum":["consent","pay","fail"]}}}}}},"responses":{"200":{"description":"The test booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceTestBooking"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/no-show":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Host/reception marks a paid, unconfirmed WORKSPACE booking a no-show once its scheduled time has passed and nobody checked in. Pays the host, does not refund the trader.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Booking marked NO_SHOW, payout created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceBooking"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/approve":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Host/reception approves a pending request-to-book, moving AWAITING_APPROVAL to PENDING_PAYMENT and creating the Stripe intent.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Booking approved, now awaiting payment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceBooking"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/{id}/decline":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Host/reception declines a pending request-to-book. Nothing was charged.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"}}}}}},"responses":{"201":{"description":"Booking cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceBooking"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/recurring":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Books the same WORKSPACE slot weekly across N occurrences — each an independent SpaceBooking sharing a recurringSeriesId, priced and paid separately. Session-authenticated only; no headless equivalent yet.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["spaceId","serviceType","scheduledAt","durationMinutes","occurrences"],"properties":{"spaceId":{"type":"string","format":"uuid"},"serviceType":{"type":"string","enum":["WORKSPACE"],"description":"Only WORKSPACE supports recurrence."},"scheduledAt":{"type":"string","format":"date-time","description":"The first occurrence's start — each later one is 7 days after the last."},"durationMinutes":{"type":"integer"},"occurrences":{"type":"integer","minimum":2,"maximum":12,"description":"First occurrence included."},"guestName":{"type":"string"},"guestCompany":{"type":"string"}}}}}},"responses":{"201":{"description":"Series created","content":{"application/json":{"schema":{"type":"object","properties":{"recurringSeriesId":{"type":"string","format":"uuid"},"bookings":{"type":"array","items":{"$ref":"#/components/schemas/SpaceBooking"}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/bookings/recurring/{seriesId}/cancel":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Cancels every still-cancellable, not-yet-occurred booking in a recurring series — the trader who made it only. A past or already-completed occurrence is left alone.\n","parameters":[{"name":"seriesId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Cancellation summary","content":{"application/json":{"schema":{"type":"object","properties":{"cancelled":{"type":"integer"},"skipped":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/spaces/{id}/calendar.ics":{"get":{"tags":["Space"],"summary":"Public, token-gated ICS calendar feed of this Space's busy windows — subscribe a calendar app directly to this URL. No TBBN session; guarded only by the unguessable token in Space.icalToken. Event summaries are generic (\"Booked\") and never carry guest names, companies, or payment details.\n","security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"token","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"An ICS (text/calendar) document","content":{"text/calendar":{"schema":{"type":"string"}}}},"404":{"description":"Calendar feed not found (wrong id or token)"}}}},"/v1/space/spaces/{id}/calendar/sync":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Host-only — fetches Space.externalCalendarUrl on demand rather than waiting for the scheduled 15-minute poll, parses its busy windows, and stores them as Space.externalBusyRanges.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Sync result","content":{"application/json":{"schema":{"type":"object","properties":{"syncedCount":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/spaces/{id}/availability":{"get":{"tags":["Space"],"summary":"A Space's bookable slots. Each open day is divided into slots of the Space's booking length, from opening time, while a whole booking fits before closing (open 8:00 to 17:00 with 30-minute bookings: 8:00–8:30, 8:30–9:00 … 16:30–17:00); a booking a day or longer has one slot a day, starting at opening. Each slot has its spots left and a state — AVAILABLE, FULL, BLOCKED (the host isn't taking bookings), TOO_SOON (inside the minimum notice), TOO_FAR (beyond how far ahead the Space takes bookings) or PAST. Days are CLOSED (not open that weekday), UNAVAILABLE (a closed date) or OPEN. A Space without hours and a booking length answers scheduled false.\n","security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","required":false,"description":"The first date, YYYY-MM-DD in the Space's time zone. Defaults to today there.","schema":{"type":"string","format":"date"}},{"name":"days","in":"query","required":false,"description":"How many days, 1 to 62. Defaults to 14.","schema":{"type":"integer","minimum":1,"maximum":62}}],"responses":{"200":{"description":"Slots","content":{"application/json":{"schema":{"type":"object","properties":{"spaceId":{"type":"string","format":"uuid"},"scheduled":{"type":"boolean"},"timeZone":{"type":"string"},"bookingLength":{"type":"object","properties":{"value":{"type":"integer"},"unit":{"type":"string","enum":["MINUTES","HOURS","DAYS","WEEKS","MONTHS"]}}},"bookingLengthLabel":{"type":"string"},"slotMinutes":{"type":["integer","null"]},"spots":{"type":"integer"},"maxAdvanceDays":{"type":["integer","null"]},"minLeadMinutes":{"type":"integer"},"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"weekday":{"type":"integer","description":"0 = Sunday"},"state":{"type":"string","enum":["OPEN","CLOSED","UNAVAILABLE"]},"label":{"type":"string"},"slots":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"state":{"type":"string","enum":["AVAILABLE","FULL","BLOCKED","TOO_SOON","TOO_FAR","PAST"]},"spotsLeft":{"type":"integer"}}}}}}}}}}}},"404":{"description":"Not found","or not visible to the caller":null},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/branches/{branchId}/schedule":{"get":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"A location's schedule for its team (bookings.view): each Space's slots for the days asked (7 by default, at most 62) with the bookings holding each slot — first name, status, spot, whether checked in — and the host's blocked times. canManage says whether the caller may block times.\n","parameters":[{"name":"branchId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":62}}],"responses":{"200":{"description":"Schedule"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/spaces/{id}/blocked-times":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Block a time on a Space's schedule — a slot, a break, a private event (spaces.manage). New bookings can't start or run into it; bookings already made aren't changed. At most 31 days per block; use blackout dates for longer closures.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["start","end"],"properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"label":{"type":"string","maxLength":80}}}}}},"responses":{"201":{"description":"The Space's blocked times"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/spaces/{id}/blocked-times/remove":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Remove every blocked time that overlaps the given time (spaces.manage).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["start","end"],"properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"}}}}}},"responses":{"200":{"description":"The Space's blocked times"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/branches/{branchId}/spot-board":{"get":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"A location's spot board — for each Space, who should be in each spot right now, who's next, and who's arriving in the next 24 hours, plus whether it's open now. For the host's team (bookings.view). Guests are shown by first name only.\n","parameters":[{"name":"branchId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Spot board","content":{"application/json":{"schema":{"type":"object","properties":{"generatedAt":{"type":"string","format":"date-time"},"businessId":{"type":"string","format":"uuid"},"businessOpeningHours":{"oneOf":[{"$ref":"#/components/schemas/OpeningHours"},{"type":"null"}]},"spaces":{"type":"array","items":{"type":"object","properties":{"spaceId":{"type":"string","format":"uuid"},"name":{"type":"string"},"status":{"type":"string"},"spotCount":{"type":"integer"},"openNow":{"type":["boolean","null"],"description":"Null when the Space has no hours."},"spots":{"type":"array","items":{"type":"object","properties":{"spotNumber":{"type":"integer"},"label":{"type":"string","example":"Table 2"},"now":{"$ref":"#/components/schemas/SpotBoardBooking"},"next":{"$ref":"#/components/schemas/SpotBoardBooking"}}}},"unassigned":{"type":"array","description":"Bookings made before spots were given out.","items":{"$ref":"#/components/schemas/SpotBoardBooking"}},"arrivals":{"type":"array","items":{"$ref":"#/components/schemas/SpotBoardBooking"}}}}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/branches/{branchId}/payouts":{"get":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Host payout history for a branch","parameters":[{"name":"branchId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Payouts","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SpacePayout"}}}}}}}},"/v1/space/search":{"get":{"tags":["Space"],"summary":"Space search — filterable by country/region/postalCode/lat+lng+radiusMiles/category/ capacity/amenity/free-text query. Without a credential it returns public Spaces. With a Space API key it returns only that key's own Business's live Spaces, of any visibility, and applies the key's saved defaults and pinned locations.\n","security":[],"parameters":[{"name":"country","in":"query","required":false,"schema":{"type":"string"}},{"name":"region","in":"query","required":false,"schema":{"type":"string"}},{"name":"postalCode","in":"query","required":false,"schema":{"type":"string"}},{"name":"category","in":"query","required":false,"description":"— \"Airbnb for Space\" browsable category.","schema":{"type":"string","enum":["STUDY_ROOM","MEETING_ROOM","OFFICE_ROOM","EVENT_SPACE","RESTAURANT_TABLE","RETAIL_CORNER","PARKING_SPOT","COWORK","OTHER"]}},{"name":"lat","in":"query","required":false,"schema":{"type":"number"}},{"name":"lng","in":"query","required":false,"schema":{"type":"number"}},{"name":"radiusMiles","in":"query","required":false,"schema":{"type":"integer"}},{"name":"query","in":"query","required":false,"schema":{"type":"string"}},{"name":"minCapacity","in":"query","required":false,"schema":{"type":"integer"}},{"name":"amenity","in":"query","required":false,"schema":{"type":"string"}},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Matching Spaces, pinned results first","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SpaceSearchResult"}}}}}}}},"/v1/branches/nearby-spaces":{"get":{"tags":["Space"],"summary":"TBBN Spaces within radiusMiles (default 200) of a point — the marketplace's own listing-time Space picker (use /v1/space/search for unbounded search). Public; a marketplace-member session additionally unlocks any RESTRICTED_EMAIL Space matching their verified email.\n","security":[],"parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number"}},{"name":"lng","in":"query","required":true,"schema":{"type":"number"}},{"name":"category","in":"query","required":false,"description":"— same browsable category filter as /v1/space/search.","schema":{"type":"string","enum":["STUDY_ROOM","MEETING_ROOM","OFFICE_ROOM","EVENT_SPACE","RESTAURANT_TABLE","RETAIL_CORNER","PARKING_SPOT","COWORK","OTHER"]}},{"name":"minCapacity","in":"query","required":false,"schema":{"type":"integer"}},{"name":"amenity","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Matching Spaces within 200 miles, nearest first, with ratings","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SpaceSearchResult"}}}}}}}},"/v1/space/api-keys":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Business owner creates a Space API key","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["businessId","environment"],"properties":{"businessId":{"type":"string","format":"uuid"},"environment":{"type":"string","enum":["SANDBOX","PRODUCTION"]}}}}}},"responses":{"201":{"description":"Key created (fullKey shown once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceApiKey"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"List a Business's Space API keys (owner-only)","parameters":[{"name":"businessId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Keys","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SpaceApiKey"}}}}}}}},"/v1/space/api-keys/{id}/rotate":{"post":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Rotate a Space API key's secret (owner-only) — keyPrefix changes, businessId does not","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Key rotated — fullKey shown once"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/space/api-keys/{id}":{"delete":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Revoke a Space API key (owner-only)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Revoked"}}}},"/v1/space/api-keys/{id}/config":{"patch":{"security":[{"BearerAuth":[]}],"tags":["Space"],"summary":"Set a Space API key's filter defaults and pinned Spaces (owner-only)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"defaultCountry":{"type":"string"},"defaultRegion":{"type":"string"},"defaultRadiusMiles":{"type":"integer"},"pinnedBranchIds":{"type":"array","items":{"type":"string","format":"uuid"}},"consentMode":{"type":"string","enum":["STRAIGHT","REQUIRE_CONSENT"]},"bookingMode":{"type":"string","enum":["INTERNAL_FREE","CHARGE_BOOKER"]}}}}}},"responses":{"200":{"description":"Config updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceApiKeyConfig"}}}}}}},"/v1/space/bookings/{id}/payment-info":{"get":{"tags":["Space"],"summary":"Backs the hosted payment page (space.tbbnetwork.com/pay/:id) — authorized either by a matching token (headless booking, no session) or a marketplace-member session naming this booking's own bookedByUserId. Returns a safe display subset only.\n","security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"token","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Booking summary","content":{"application/json":{"schema":{"type":"object","properties":{"branchName":{"type":"string"},"spaceName":{"type":"string"},"serviceType":{"type":"string","enum":["MEETUP","DROPOFF_PICKUP","WORKSPACE"]},"grossAmount":{"type":"number"},"currency":{"type":"string"},"status":{"type":"string"}}}}}},"404":{"description":"Not found, or neither token nor session authorizes this booking"}}}},"/v1/reputation/reviews":{"post":{"tags":["Reputation"],"summary":"Submit 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.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["targetType","rating"],"properties":{"targetType":{"type":"string","enum":["MERCHANT","SPACE_BRANCH"]},"merchantId":{"type":"string","format":"uuid"},"branchId":{"type":"string","format":"uuid"},"rating":{"type":"integer","minimum":1,"maximum":5},"comment":{"type":"string"},"tradeSessionId":{"type":"string","format":"uuid"},"spaceBookingId":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Review created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Review"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/reputation/merchants/{id}/reviews":{"get":{"tags":["Reputation"],"summary":"A Merchant's reviews plus aggregate rating","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Reviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewsList"}}}}}}},"/v1/reputation/branches/{id}/reviews":{"get":{"tags":["Reputation"],"summary":"A Space Branch's reviews plus aggregate rating","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Reviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewsList"}}}}}}},"/v1/reputation/businesses/{id}/reviews-summary":{"get":{"security":[],"tags":["Reputation"],"summary":"Aggregate rating across every Merchant and Branch linked to a Business.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Summary","content":{"application/json":{"schema":{"type":"object","properties":{"averageRating":{"type":["number","null"]},"reviewCount":{"type":"integer"}}}}}}}}},"/v1/merchants":{"post":{"tags":["Merchants"],"summary":"Apply for a Merchant account for one of your Businesses (creates a 7-day temp account)","description":"Signed in as the Business owner or an Admin of it. The Business must be a verified, registered business.\n","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantApplicationInput"}}}},"responses":{"201":{"description":"Application created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Merchant"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/merchants/{id}":{"get":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"Get merchant","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Merchant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Merchant"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"patch":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"Update merchant settings (ADMIN/OWNER only). Once status is APPROVED, also requires a step-up token scoped to \"merchant:update\" — see POST /v1/auth/step-up/confirm. A still-pending/under-review Merchant needs no step-up token.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"X-TBBN-Step-Up-Token","in":"header","required":false,"schema":{"type":"string"},"description":"Required only once this Merchant's status is APPROVED."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Only the fields below are applied; anything else in the body (tier, status, billing or suspension state, relations) is ignored. Requires a signed-in merchant representative with the OWNER or ADMIN role — an API key can't change a Merchant's own settings — plus the step-up token header once the Merchant is APPROVED.","properties":{"legalName":{"type":"string"},"displayName":{"type":"string"},"logoUrl":{"type":["string","null"],"format":"uri","description":"An https image URL (upload it with POST /v1/media/upload-url, folder merchants), or null to remove the logo."},"slug":{"type":"string"},"country":{"type":"string"},"defaultCurrency":{"type":"string"},"matchingEnabled":{"type":"boolean","description":"See Merchant.matchingEnabled."},"checkoutEnabled":{"type":"boolean","description":"See Merchant.checkoutEnabled."},"counterpartyHoldDays":{"type":["integer","null"],"description":"See Merchant.counterpartyHoldDays."},"autoCloseUnreciprocatedTrades":{"type":"boolean"},"commissionRate":{"type":"number","minimum":0,"maximum":100,"description":"See Merchant.commissionRate. Up to two decimals; can be changed but not cleared. Each change is kept in GET /v1/merchants/{id}/commission-rates."},"passCheckoutFee":{"type":"boolean","description":"See Merchant.passCheckoutFee."},"passProcessingFees":{"type":"boolean","description":"See Merchant.passProcessingFees."},"shipFromRegion":{"type":["string","null"],"maxLength":80,"description":"See Merchant.shipFromRegion."},"checkoutEndpointUrl":{"type":["string","null"],"format":"uri","description":"The trade checkout link (https): where a trader is sent to pay for this Merchant's items. tbbn_trade_session_id and tbbn_side are added to it. Required, with commissionRate, before the Merchant's items can be traded."}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Merchant"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/merchants/{id}/commission-rates":{"get":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"Every commission rate the Merchant has set, newest first.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Rate history","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"rate":{"type":"string"},"effectiveFrom":{"type":"string","format":"date-time"},"setByUserId":{"type":["string","null"],"format":"uuid"}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/merchants/{id}/submit-application":{"post":{"tags":["Merchants"],"summary":"Submit or resubmit onboarding documents during the 7-day temp window","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Submitted for review"},"410":{"description":"Temp account expired"}}}},"/v1/merchants/{id}/application-status":{"get":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"Track onboarding application status","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Status","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["SUBMITTED","RESUBMISSION_REQUIRED","UNDER_REVIEW","APPROVED","REJECTED","EXPIRED"]},"reviewerNotes":{"type":["string","null"]}}}}}}}}},"/v1/merchants/{id}/users/invite":{"post":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"Invite someone to the team with a role (Owner or Admin)","description":"Emails an invitation; the person joins with the role when they accept it while signed in with that email on tbbnetwork.com (they create a free account first if they have none). Inviting the same email again replaces their open invitation.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","role"],"properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["ADMIN","DEVELOPER","ACCOUNT_MANAGER","OPERATIONS","ACCOUNTANT","SUPPORT","VIEWER"]}}}}}},"responses":{"201":{"description":"Invitation sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamInvitation"}}}}}}},"/v1/merchants/{id}/invitations":{"get":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"The team's invitations waiting for an answer (Owner or Admin)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Invitations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TeamInvitation"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/merchants/{id}/invitations/{invitationId}":{"delete":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"Withdraw an invitation (Owner or Admin)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"invitationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Withdrawn","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamInvitation"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/merchants/invitations/mine":{"get":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"Invitations to Merchant teams waiting for you","responses":{"200":{"description":"Your invitations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MyTeamInvitation"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/merchants/invitations/{invitationId}/accept":{"post":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"Accept an invitation and join the Merchant's team with its role","description":"Signed in with the invited email.","parameters":[{"name":"invitationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Joined","content":{"application/json":{"schema":{"type":"object","properties":{"merchantId":{"type":"string","format":"uuid"},"merchantName":{"type":"string"},"role":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/merchants/invitations/{invitationId}/decline":{"post":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"Decline an invitation","parameters":[{"name":"invitationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Declined"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/merchants/{id}/users":{"get":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"List merchant org users","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Users","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MerchantUser"}},"nextCursor":{"type":["string","null"]}}}}}}}}},"/v1/merchants/{id}/users/{userId}/role":{"patch":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"Change a team member's role (Owner or Admin; the Owner's role can't change)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["role"],"properties":{"role":{"type":"string","enum":["ADMIN","DEVELOPER","ACCOUNT_MANAGER","OPERATIONS","ACCOUNTANT","SUPPORT","VIEWER"]}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantUser"}}}}}}},"/v1/merchants/{id}/users/{userId}":{"delete":{"security":[{"BearerAuth":[]}],"tags":["Merchants"],"summary":"Remove a team member (Owner or Admin; the Owner can't be removed)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Removed"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/api-keys":{"post":{"security":[{"BearerAuth":[]}],"tags":["ApiKeys"],"summary":"Create an API key (ADMIN/OWNER only; production keys require ADMIN+ approval)","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["environment","scopes"],"properties":{"environment":{"type":"string","enum":["SANDBOX","PRODUCTION"],"description":"SANDBOX (an sk_sandbox_ test key) only in test mode, on the Merchant's test account; PRODUCTION only on the live account once it's approved. Otherwise 409 TEST_MODE_REQUIRED or LIVE_MODE_REQUIRED.\n"},"scopes":{"type":"array","items":{"type":"string"},"description":"Empty for the whole API, or the API areas the key may call — the first path segment after /v1/, e.g. listings, offers, trade-sessions.\n"}}}}}},"responses":{"201":{"description":"Key created — fullKey shown once","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreated"}}}}}},"get":{"security":[{"BearerAuth":[]}],"tags":["ApiKeys"],"summary":"List API keys (never returns the full key, only the prefix)","parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Keys","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}},"nextCursor":{"type":["string","null"]}}}}}}}}},"/v1/api-keys/{id}/rotate":{"post":{"security":[{"BearerAuth":[]}],"tags":["ApiKeys"],"summary":"Rotate a key (old key marked ROTATED, still valid for a grace window)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"New key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreated"}}}}}}},"/v1/api-keys/{id}":{"delete":{"security":[{"BearerAuth":[]}],"tags":["ApiKeys"],"summary":"Revoke a key immediately","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Revoked"}}}},"/merchant/sellers/verify":{"post":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"Merchant-facing: register or match a seller after the MERCHANT has verified them. See the Glossary guide \"Identity federation\" — sellers never call this directly.\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SellerVerifyInput"}}}},"responses":{"200":{"description":"Verified/linked, or a cross-merchant match requiring the OAuth account-linking handshake","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SellerVerifyResult"}}}}}}},"/merchant/sellers/headless":{"post":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"Merchant-facing: create a headless Seller — a trade participant with no linked TBBN account, gated by the calling Merchant's plan tier.\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateHeadlessSellerInput"}}}},"responses":{"200":{"description":"Created (or the existing link, if idempotent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Seller"}}}},"422":{"description":"This merchant's plan tier does not allow headless Sellers"}}}},"/merchant/sellers/link-code":{"post":{"tags":["Sellers"],"summary":"Redeem a customer's TBBN link code — the one-time code they made in their TBBN account (Linked stores → Link a store) and entered in their account on your site. Links their TBBN account to your store as the OAuth handshake does, under your reference for them (merchant_seller_ref, optional — without it the link is keyed by their TBBN seller id). A code works once, for 30 minutes; a failed link (409) leaves it usable.\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"code":{"type":"string","maxLength":20,"example":"7Q4K-9M2X"},"merchant_seller_ref":{"type":"string","maxLength":200}}}}}},"responses":{"201":{"description":"Linked","content":{"application/json":{"schema":{"type":"object","properties":{"sellerId":{"type":"string","format":"uuid"},"merchantSellerRef":{"type":"string"},"linked":{"type":"boolean"},"attributedListings":{"type":"integer","description":"Your listings under that reference that are now theirs."}}}}}},"400":{"description":"LINK_CODE_INVALID — wrong, used or expired"},"409":{"description":"SELLER_REF_LINKED_TO_ANOTHER — that reference is linked to someone else"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/oauth-clients":{"post":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"Register a Merchant's account-linking OAuth client","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOAuthClientInput"}}}},"responses":{"200":{"description":"Created — clientSecret is shown only in this response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthClient"}}}}}},"get":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"List a Merchant's OAuth clients","parameters":[{"name":"merchantId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OAuth clients","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OAuthClient"}}}}}}}},"/v1/oauth-clients/{id}/rotate-secret":{"post":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"Rotate an OAuth client's secret — the clientId is unchanged","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Rotated — the new clientSecret is shown only in this response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthClient"}}}}}}},"/v1/oauth-clients/{id}":{"delete":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"Revoke an OAuth client","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Revoked"}}}},"/v1/link/oauth/clients/{clientId}":{"get":{"tags":["Sellers"],"summary":"Public client metadata — used by TBBN's account consent screen to validate redirect_uri and render \"Merchant X wants to link your TBBN profile\" before showing Approve/Deny.\n","parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Client metadata","content":{"application/json":{"schema":{"type":"object","properties":{"clientId":{"type":"string"},"redirectUris":{"type":"array","items":{"type":"string","format":"uri"}},"merchantDisplayName":{"type":["string","null"]}}}}}}}}},"/v1/link/oauth/consent":{"post":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"Step 1 of the OAuth account-linking handshake — the logged-in member approves. Creates a short-lived, single-use authorization code; the caller (the consent page) performs the actual redirect.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthConsentInput"}}}},"responses":{"200":{"description":"Authorization code issued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthConsentResult"}}}}}}},"/v1/link/oauth/token":{"post":{"tags":["Sellers"],"summary":"Exchange an account-linking authorization code for the link — from your server, within 5 minutes, once. Returns the seller's id; no access or refresh token is issued. See the Seller account linking guide.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthTokenInput"}}}},"responses":{"200":{"description":"Linked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthTokenResult"}}}}}}},"/v1/sellers/me/links":{"get":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"List the stores the signed-in member's account is linked to","responses":{"200":{"description":"Linked stores","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"merchantId":{"type":"string","format":"uuid"},"merchantName":{"type":"string"},"linkedAt":{"type":"string","format":"date-time"},"linkMethod":{"type":"string"}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/sellers/me/link-codes":{"post":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"Make a one-time link code (30 minutes) the signed-in member gives a store to link their account. At most five unused at once.\n","responses":{"201":{"description":"The code","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"7Q4K-9M2X"},"expiresAt":{"type":"string","format":"date-time"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/sellers/me/storefront":{"get":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"The signed-in member's storefront address (tbbnetwork.com/store/{slug}) and bio; the address is made from their name the first time.\n","responses":{"200":{"description":"The storefront settings","content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string"},"bio":{"type":["string","null"]}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"patch":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"Change the storefront's address or bio","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","maxLength":40,"description":"3–40 lowercase letters, numbers and dashes (409 STORE_ADDRESS_TAKEN)."},"bio":{"type":"string","maxLength":280,"description":"No contact details."}}}}}},"responses":{"200":{"description":"Saved"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/sellers/me/linkable-stores":{"get":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"Stores where a member connects their TBBN account on the store's own site","responses":{"200":{"description":"Stores with their linking page","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"merchantId":{"type":"string","format":"uuid"},"name":{"type":"string"},"logoUrl":{"type":["string","null"],"format":"uri"},"linkAccountUrl":{"type":"string","format":"uri"}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/sellers/me/links/{merchantId}":{"delete":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"Unlink the signed-in member from a store","description":"That store's listings for the member stop showing on TBBN. Refused (409) while a trade through that store is in progress.\n","parameters":[{"name":"merchantId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Unlinked"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/sellers":{"get":{"tags":["Sellers"],"summary":"List your linked sellers, newest link first","parameters":[{"name":"merchantId","in":"query","required":true,"description":"Your own Merchant id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Your sellers","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"sellerId":{"type":"string","format":"uuid"},"name":{"type":["string","null"]},"merchantSellerRef":{"type":["string","null"],"description":"Your own reference for this customer."},"linkedMerchantCount":{"type":"integer","description":"How many stores this seller is linked to on TBBN."},"federated":{"type":"boolean","description":"The seller trades through more than one store on TBBN."}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/sellers/{id}/unlink":{"post":{"security":[{"BearerAuth":[]}],"tags":["Sellers"],"summary":"Unlink one of your sellers. Your live listings for that seller come down; refused (409) while a trade through your store is in progress.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["merchantId"],"properties":{"merchantId":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Unlinked"}}}},"/merchant/listings":{"get":{"tags":["Listings"],"summary":"Your own catalogue — every listing you've sent, in any status but deleted (live, in a trade, taken down, sold, held for review, removed by TBBN), newest first, with counts per status. Search titles and your own references with q. A team member signed in to the dashboard can always read it; changing a listing needs a role with the catalogue permission.\n","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ACTIVE","RESERVED","SOLD","UNAVAILABLE","PENDING_REVIEW","REMOVED"]}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"ref","in":"query","required":false,"description":"Only the listing with exactly this merchant listing reference (your own id for the item, case-sensitive) — to check whether an item is already on TBBN before sending it.\n","schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100}}],"responses":{"200":{"description":"Your listings","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"merchantListingRef":{"type":["string","null"]},"category":{"type":"string"},"status":{"type":"string"},"originalPrice":{"type":"string"},"currency":{"type":"string"},"images":{"type":"array","items":{"type":"string"}},"fromFeed":{"type":"boolean"},"feedDelistedAt":{"type":["string","null"],"format":"date-time"},"syncedAt":{"type":"string","format":"date-time"},"seller":{"type":["object","null"]},"sellerRef":{"type":["string","null"],"description":"Your own reference for the customer whose item it is."}}}},"nextCursor":{"type":["string","null"]},"counts":{"type":"object","additionalProperties":{"type":"integer"},"description":"Listings per status."}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"post":{"tags":["Listings"],"summary":"Create or sync a listing. Upserts on (merchantId, merchantListingRef) — a repeat call with the same ref updates the existing listing rather than creating a duplicate. A listing sync is counted when this creates or changes a listing; sending it again unchanged is free. See the integration walkthrough's \"Sync your listings\".\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertListingInput"}}}},"responses":{"201":{"description":"Listing created or updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Listing"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/merchant/listings/{id}":{"put":{"tags":["Listings"],"summary":"Update a listing by id (partial — only supplied fields change)","description":"Counts as a listing sync when it changes the listing; an update that changes nothing is not written or counted.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertListingInput"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Listing"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"delete":{"tags":["Listings"],"summary":"Soft-delete a listing","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Deleted"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/merchant/listings/{id}/availability":{"post":{"tags":["Listings"],"summary":"Report an availability change for one of your listings — SOLD or UNAVAILABLE when it sells or goes out of stock elsewhere, ACTIVE when it's available again. TBBN sets RESERVED itself while the item is part of a trade.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["ACTIVE","RESERVED","SOLD","UNAVAILABLE"]}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Listing"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/merchant/listings/{id}/wants":{"put":{"tags":["Listings"],"summary":"Replace a listing's wants array (full replace, not merge) — see the Glossary guide \"Wants\"","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["wants"],"properties":{"wants":{"type":"array","items":{"$ref":"#/components/schemas/Want"},"maxItems":20}}}}}},"responses":{"200":{"description":"Replaced","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Want"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/listings/{id}/wants":{"get":{"security":[],"tags":["Listings"],"summary":"Read a listing's wants array","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Wants","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Want"}}}}}}}},"/v1/merchant/feed-source":{"get":{"tags":["Listings"],"summary":"This merchant's catalog feed configuration — see the Catalog feed guide.","responses":{"200":{"description":"Feed source config (feedPassword never included in the response)"},"404":{"description":"No feed source configured yet"},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"post":{"tags":["Listings"],"summary":"Create or replace this merchant's feed source config","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["feedUrl","feedFormat"],"properties":{"feedUrl":{"type":"string"},"feedFormat":{"type":"string","enum":["XML","CSV"]},"feedUsername":{"type":"string"},"feedPassword":{"type":"string","description":"Sent as HTTP Basic Auth when TBBN fetches feedUrl."},"defaultSellerId":{"type":"string","format":"uuid","description":"Optional. One of your own sellers, credited with every feed row that has no external_seller_id. Without it, those rows are your store's own items."},"enabled":{"type":"boolean"}}}}}},"responses":{"201":{"description":"Feed source saved"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/merchant/feed-source/fetch-now":{"post":{"tags":["Listings"],"summary":"Manual fetch, doesn't wait for the 24h scheduled cycle — same escape hatch Google/Meta's own scheduled-feed UIs both offer.","responses":{"201":{"description":"Sync attempted (see response body for success/failure)"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/listings/{id}":{"get":{"tags":["Listings"],"summary":"Get a listing","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Listing"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/listings":{"get":{"tags":["Listings"],"summary":"Directory-lite listing read across all visibilities the caller is entitled to — full ranked search lives in Search/Matching","parameters":[{"name":"category","in":"query","required":false,"schema":{"type":"string"}},{"name":"merchantId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"visibility","in":"query","required":false,"schema":{"type":"string","enum":["PRIVATE","GLOBAL","BOTH","ENTERPRISE"]}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Listings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListingPage"}}}}}}},"/v1/listings/individual":{"post":{"security":[{"BearerAuth":[]}],"tags":["Listings"],"summary":"Create a listing of your own as a signed-in member (no merchant). The seller is always the signed-in member.\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIndividualListingInput"}}}},"responses":{"200":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Listing"}}}}}}},"/v1/listings/mine":{"get":{"security":[{"BearerAuth":[]}],"tags":["Listings"],"summary":"Every listing the signed-in member created, in every status except deleted. The public listing read returns only what is available; this is how an owner sees their Pending and Sold items.\n","responses":{"200":{"description":"The caller's listings, newest first","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Listing"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/listings/individual/{id}/availability":{"post":{"security":[{"BearerAuth":[]}],"tags":["Listings"],"summary":"Set one of your own listings to Available (ACTIVE), Pending (RESERVED) or Sold. Only the member who created the listing may change it; merchant-synced listings are managed on the merchant's platform.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["ACTIVE","RESERVED","SOLD","UNAVAILABLE"]}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Listing"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/listings/individual/{id}":{"put":{"security":[{"BearerAuth":[]}],"tags":["Listings"],"summary":"Edit one of your own listings","description":"Replaces the details of a listing the signed-in member created (the same fields as creating one). A merchant's listing can't be edited here, even when it is linked to your account — it changes only where the merchant listed it (403). Not while the item is reserved in a trade (409). Edits go through the same prohibited-items and contact-details checks as a new listing; who can see the listing stays as created.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIndividualListingInput"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Listing"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"delete":{"security":[{"BearerAuth":[]}],"tags":["Listings"],"summary":"Remove one of your own listings","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Removed"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/platform/status":{"get":{"tags":["Platform"],"summary":"Launch state of the platform and any per-country overrides","security":[],"responses":{"200":{"description":"Launch status","content":{"application/json":{"schema":{"type":"object","properties":{"launchState":{"type":"string","enum":["PRELAUNCH","LIVE"]},"launchedAt":{"type":["string","null"],"format":"date-time"},"countryOverrides":{"type":"object","additionalProperties":{"type":"string","enum":["PRELAUNCH","LIVE"]}}}}}}}}}},"/v1/platform/markets":{"get":{"tags":["Platform"],"summary":"Countries with their own site on tbbnetwork.com","description":"tbbnetwork.com is served per country — the default site first, then every country TBBN has launched in. A visitor from any other country is served by the default site.\n","security":[],"responses":{"200":{"description":"Country sites","content":{"application/json":{"schema":{"type":"object","required":["defaultCountry","countries"],"properties":{"defaultCountry":{"type":"string","example":"US"},"countries":{"type":"array","items":{"type":"string","example":"US"}}}}}}}}}},"/v1/platform/availability":{"get":{"tags":["Platform"],"summary":"Where TBBN is available, and which services each country has","description":"Every country TBBN lists as available, with the services open there. live is false while a country is still launching. Prices are not included — see GET /v1/platform/pricing.\n","security":[],"responses":{"200":{"description":"Countries and their services","content":{"application/json":{"schema":{"type":"object","required":["countries"],"properties":{"countries":{"type":"array","items":{"type":"object","required":["country","live","services"],"properties":{"country":{"type":"string","example":"DE","description":"ISO 3166-1 alpha-2"},"live":{"type":"boolean"},"services":{"type":"object","properties":{"marketplaceListings":{"type":"boolean"},"merchantOnboarding":{"type":"boolean"},"spaceHosting":{"type":"boolean"},"acceptPayments":{"type":"boolean"},"payouts":{"type":"boolean"},"identityVerification":{"type":"boolean"},"businessVerification":{"type":"boolean"}}}}}}}}}}}}}},"/v1/platform/countries/{country}":{"get":{"tags":["Platform"],"summary":"What TBBN offers in a country right now — identity verification, business verification, merchant accounts, Space hosting, payments, payouts, marketplace listings — plus the per-country form configuration (labels and required fields) every onboarding form reads.\n","security":[],"parameters":[{"name":"country","in":"path","required":true,"description":"ISO 3166-1 alpha-2","schema":{"type":"string","minLength":2,"maxLength":2}}],"responses":{"200":{"description":"Capabilities for the country","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CountryCapabilities"}}}}}}},"/v1/platform/pricing":{"get":{"tags":["Platform"],"summary":"What TBBN charges in a country — plans, usage rates, top-ups and Space fees","description":"Prices in the country's own currency where TBBN has set them, otherwise in USD. Percent prices are percentages. Without a country, the prices for where the request comes from.\n","security":[],"parameters":[{"name":"country","in":"query","required":false,"description":"ISO 3166-1 alpha-2","schema":{"type":"string","minLength":2,"maxLength":2}}],"responses":{"200":{"description":"Prices for the country","content":{"application/json":{"schema":{"type":"object","properties":{"country":{"type":["string","null"]},"currency":{"type":"string","description":"ISO 4217"},"local":{"type":"boolean","description":"True when these are the country's own prices rather than USD."},"prices":{"type":"object","additionalProperties":{"type":"number"}},"labels":{"type":"object","additionalProperties":{"type":"string"}}}}}}}}}},"/v1/blog/posts":{"get":{"tags":["Blog"],"summary":"Published blog posts, sorted and filtered","security":[],"parameters":[{"name":"sort","in":"query","description":"recommended (featured first, then engagement weighted by recency), latest, popular or discussed.","schema":{"type":"string","enum":["recommended","latest","popular","discussed"]}},{"name":"category","in":"query","schema":{"type":"string"}},{"name":"tag","in":"query","schema":{"type":"string"}},{"name":"q","in":"query","description":"Words in the title","excerpt or tags.":null,"schema":{"type":"string"}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":48}}],"responses":{"200":{"description":"One page of posts","content":{"application/json":{"schema":{"type":"object","properties":{"sort":{"type":"string"},"page":{"type":"integer"},"pages":{"type":"integer"},"total":{"type":"integer"},"data":{"type":"array","items":{"$ref":"#/components/schemas/BlogPostCard"}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/taxonomy":{"get":{"tags":["Blog"],"summary":"Categories and tags used on published posts, with post counts","security":[],"responses":{"200":{"description":"Categories and tags"}}}},"/v1/blog/index":{"get":{"tags":["Blog"],"summary":"Every published post's address and dates, for sitemaps and feeds","security":[],"responses":{"200":{"description":"Post slugs","titles":null,"excerpts and dates":null}}}},"/v1/blog/posts/{slug}":{"get":{"tags":["Blog"],"summary":"One published post, with related posts","description":"With a member's session, likedByMe says whether they liked it.","security":[],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The post","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BlogPostCard"},{"type":"object","properties":{"body":{"type":"string","description":"Markdown"},"seoTitle":{"type":["string","null"]},"seoDescription":{"type":["string","null"]},"updatedAt":{"type":"string","format":"date-time"},"likedByMe":{"type":"boolean"},"related":{"type":"array","items":{"$ref":"#/components/schemas/BlogPostCard"}}}}]}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/posts/{slug}/comments":{"get":{"tags":["Blog"],"summary":"A post's comments, with replies under the comment they answer","security":[],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Comments"},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"post":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Comment on a post, or reply to a comment","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body"],"properties":{"body":{"type":"string","minLength":1,"maxLength":2000},"parentId":{"type":"string","format":"uuid","description":"The comment you're replying to."}}}}}},"responses":{"200":{"description":"The comment"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/posts/{slug}/like":{"put":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Like a post (liking twice changes nothing)","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"liked and likeCount"},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"delete":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Take back a like","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"liked and likeCount"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/posts/{slug}/view":{"post":{"tags":["Blog"],"summary":"Count a read of a post (send once a visit)","security":[],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Counted"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/posts/{slug}/share":{"post":{"tags":["Blog"],"summary":"Count a share of a post","security":[],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string","description":"e.g. x","linkedin":null,"copy-link":null}}}}}},"responses":{"200":{"description":"Counted"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/comments/{id}":{"delete":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Delete your own comment","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/manage/me":{"get":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Your blog role (AUTHOR or EDITOR) and publishing name; 403 if you don't write for the blog","responses":{"200":{"description":"Your role, publishing name and account name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogWriterProfile"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"patch":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Set the name you publish under","description":"The public sees this name on your posts and comments instead of your account name. 2 to 60 characters — letters, numbers, spaces, full stops, apostrophes and hyphens — and not already used by another writer. Send null or an empty string to publish under your account name again.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"penName":{"type":["string","null"],"maxLength":60,"example":"Ada K."}}}}}},"responses":{"200":{"description":"Your updated profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogWriterProfile"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/manage/posts":{"get":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Posts you can manage — your own as an author, every post as an editor","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["DRAFT","SCHEDULED","PUBLISHED","ARCHIVED"]}}],"responses":{"200":{"description":"Posts"},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"post":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Start a post (as a draft)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPostInput"}}}},"responses":{"200":{"description":"The draft"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/manage/posts/{id}":{"get":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"A post you can manage, in full","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The post"},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"patch":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Edit a post","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPostInput"}}}},"responses":{"200":{"description":"The post"},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"delete":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Delete a post, with its likes and comments (editors; authors only their drafts)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/manage/posts/{id}/publish":{"post":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Publish a post now, or schedule it with at","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"at":{"type":"string","format":"date-time"}}}}}},"responses":{"200":{"description":"The post"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/manage/posts/{id}/unpublish":{"post":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Take a post off the blog (back to a draft, or archived if it was public)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The post"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/manage/comments":{"get":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Comments to moderate on the posts you manage","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["VISIBLE","HIDDEN"]}},{"name":"postId","in":"query","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Comments"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/blog/manage/comments/{id}":{"patch":{"security":[{"BearerAuth":[]}],"tags":["Blog"],"summary":"Hide a comment, or show it again","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["VISIBLE","HIDDEN"]}}}}}},"responses":{"200":{"description":"Updated"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/agreements/{kind}/current":{"get":{"tags":["Platform"],"summary":"The current version of an agreement (business-profile, merchant, space-host, trade-terms)","security":[],"parameters":[{"name":"kind","in":"path","required":true,"schema":{"type":"string","enum":["business-profile","merchant","space-host","trade-terms"]}}],"responses":{"200":{"description":"Agreement text and version","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Agreement"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/agreements/status":{"get":{"security":[{"BearerAuth":[]}],"tags":["Platform"],"summary":"Which agreements the signed-in person has accepted at their current version","responses":{"200":{"description":"Per-kind acceptance status","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"object","properties":{"title":{"type":"string"},"currentVersion":{"type":["integer","null"]},"accepted":{"type":"boolean"}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/agreements/{kind}/accept":{"post":{"security":[{"BearerAuth":[]}],"tags":["Platform"],"summary":"Accept the current version of an agreement. Idempotent per person and version.","parameters":[{"name":"kind","in":"path","required":true,"schema":{"type":"string","enum":["business-profile","merchant","space-host","trade-terms"]}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"businessId":{"type":"string","format":"uuid"},"merchantId":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Recorded","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"boolean"},"version":{"type":"integer"},"acceptedAt":{"type":"string","format":"date-time"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/identity/status":{"get":{"security":[{"BearerAuth":[]}],"tags":["Platform"],"summary":"The signed-in person's identity verification status — unverified, pending, verified or needs attention — whether verification is offered in their country, and their latest attempt. A pending attempt is refreshed from the provider on read.\n","responses":{"200":{"description":"Identity status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityStatus"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/identity/sessions":{"post":{"security":[{"BearerAuth":[]}],"tags":["Platform"],"summary":"Start an identity check (government ID + selfie). Returns the hosted page to send the person to; they come back to the return URL when done and the status endpoint reflects the outcome. Only where the country access matrix offers identity verification.\n","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"returnUrl":{"type":"string","format":"uri","description":"Must be on tbbnetwork.com; defaults to the account's verify page."},"country":{"type":"string","minLength":2,"maxLength":2,"description":"Needed only when the account has no country on file yet."}}}}}},"responses":{"201":{"description":"Check opened","content":{"application/json":{"schema":{"type":"object","properties":{"verificationId":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"}}}}}},"403":{"description":"Identity verification is not available in the person's country yet"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/waitlist/demo-requests":{"post":{"tags":["Listings"],"summary":"Request a product demo (public — no credential required)","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","email","businessName","services"],"properties":{"name":{"type":"string","maxLength":120},"email":{"type":"string","format":"email"},"businessName":{"type":"string","maxLength":160},"website":{"type":"string","maxLength":200},"phone":{"type":"string","maxLength":40},"role":{"type":"string","maxLength":80},"services":{"type":"array","minItems":1,"items":{"type":"string","enum":["MARKETPLACE_INTEGRATION","SPACE","DEVELOPER_API","ENTERPRISE","EVERYTHING"]}},"message":{"type":"string","maxLength":2000},"source":{"type":"string","maxLength":200}}}}}},"responses":{"202":{"description":"Received","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/catalog/categories":{"get":{"tags":["Catalog"],"summary":"List all top-level categories in the taxonomy","description":"Every listing belongs to one of these categories and, within it, one of its subcategories. Send these names when you create or update a listing. A name TBBN doesn't recognise is placed in the closest category it can match, or in Other.\n","security":[],"responses":{"200":{"description":"Categories","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}}}},"/v1/catalog/categories/{category}/subcategories":{"get":{"tags":["Catalog"],"summary":"List subcategories under a category","security":[],"parameters":[{"name":"category","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Subcategories","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/catalog/brands":{"get":{"tags":["Catalog"],"summary":"List brands, optionally scoped to a category and/or subcategory","security":[],"parameters":[{"name":"category","in":"query","required":false,"schema":{"type":"string"}},{"name":"subcategory","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Brands","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/media/ingest":{"post":{"tags":["Media"],"summary":"Copy listing images from public https:// addresses (JPEG, PNG, WebP or GIF, decided from the image itself, so an address needs no file extension; at most 5 per call; each is resized to fit 1600px and stored as WebP) into TBBN-hosted URLs for a listing's images array. Every image is tried. If any can't be imported, the call returns 400 IMAGE_IMPORT_FAILED with details.images listing each one — ok, or the stage that failed (address, download, size, decode, store), whether retrying may work, and why — and details.retryable true when every failure is worth retrying. A refusal is not kept against your Idempotency-Key, so once the cause is fixed a retry with the same key runs again.\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["urls"],"properties":{"urls":{"type":"array","items":{"type":"string","format":"uri"},"maxItems":10}}}}}},"responses":{"201":{"description":"Ingested","content":{"application/json":{"schema":{"type":"object","properties":{"images":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]}}}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/media/upload":{"post":{"security":[{"BearerAuth":[]}],"tags":["Media"],"summary":"Upload a photo through TBBN, base64 in data — for when a direct upload to the presigned URL from POST /v1/media/upload-url isn't possible. The photo is resized to fit 1600px and stored as WebP. Same callers as upload-url.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["folder","data"],"properties":{"folder":{"type":"string","enum":["listings","spaces","bookings","merchants","blog","avatars"]},"data":{"type":"string","description":"The photo's bytes","base64. At most 8 MB.":null},"bookingId":{"type":"string","format":"uuid"},"token":{"type":"string"}}}}}},"responses":{"201":{"description":"Stored","content":{"application/json":{"schema":{"type":"object","properties":{"publicUrl":{"type":"string","format":"uri"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/media/upload-url":{"post":{"security":[{"BearerAuth":[]}],"tags":["Media"],"summary":"Get a short-lived upload URL for an image. The browser uploads the file directly to it. PUT the file to `uploadUrl` with the same Content-Type, then use `publicUrl` wherever an image URL is needed (e.g. a Listing's images array).\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contentType","folder"],"properties":{"contentType":{"type":"string","description":"One of image/jpeg, image/png, image/webp, image/gif."},"folder":{"type":"string","enum":["listings","spaces","bookings","merchants","blog","avatars"]},"bookingId":{"type":"string","format":"uuid","description":"Without a session, for a drop-off booking's handover photo: the booking, with token. Only for the bookings folder."},"token":{"type":"string","description":"The token from the booking's link."}}}}}},"responses":{"201":{"description":"Presigned upload URL","content":{"application/json":{"schema":{"type":"object","properties":{"uploadUrl":{"type":"string","format":"uri"},"publicUrl":{"type":"string","format":"uri"},"key":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/directory/listings":{"get":{"tags":["Directory"],"summary":"Public global-directory listing read. Only GLOBAL/BOTH-visibility listings ever appear here — PRIVATE and ENTERPRISE never do. See the Glossary guide \"Global vs Private vs Both vs Enterprise\".\n","security":[],"parameters":[{"name":"category","in":"query","required":false,"schema":{"type":"string"}},{"name":"q","in":"query","required":false,"description":"Words to find in the title, brand, category or model.","schema":{"type":"string","maxLength":100}},{"name":"swap","in":"query","required":false,"description":"1 for only listings open to swaps (TRADE or BOTH).","schema":{"type":"string","enum":["1"]}},{"name":"sort","in":"query","required":false,"description":"newest, price_asc or price_desc. Without it, newest first.","schema":{"type":"string","enum":["newest","price_asc","price_desc"]}},{"name":"buy","in":"query","required":false,"description":"1 for only listings that can be bought (SALE or BOTH).","schema":{"type":"string","enum":["1"]}},{"name":"minPrice","in":"query","required":false,"description":"Lowest listed price, in the listing's own currency.","schema":{"type":"number","minimum":0}},{"name":"maxPrice","in":"query","required":false,"description":"Highest listed price, in the listing's own currency.","schema":{"type":"number","minimum":0}},{"name":"seller","in":"query","required":false,"description":"store for only stores' listings, person for only people's own.","schema":{"type":"string","enum":["store","person"]}},{"name":"page","in":"query","required":false,"description":"A page number, from 1, instead of a cursor. The response's page and pageCount say where you are.\n","schema":{"type":"integer","minimum":1}},{"name":"matchFor","in":"query","required":false,"description":"One of the signed-in member's own listings open to swaps. Only listings it could be swapped for are returned (TRADE or BOTH, other sellers, same currency), each with compatibilityScore and compatibilityBreakdown, best match first unless sort is set; the response's matchedFor names it. Ignored for anyone else's listing.\n","schema":{"type":"string","format":"uuid"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["GOODS","VEHICLE","PROPERTY","SERVICE","TICKET"]}},{"name":"make","in":"query","required":false,"description":"A vehicle's make (any case).","schema":{"type":"string","maxLength":60}},{"name":"subcategory","in":"query","required":false,"description":"A subcategory of the category asked for, from GET /v1/catalog/categories/{category}/subcategories (any case).\n","schema":{"type":"string","maxLength":80}},{"name":"brand","in":"query","required":false,"description":"A brand (a vehicle's make), from GET /v1/catalog/brands (any case).","schema":{"type":"string","maxLength":60}},{"name":"minYear","in":"query","required":false,"schema":{"type":"integer"}},{"name":"maxYear","in":"query","required":false,"schema":{"type":"integer"}},{"name":"minBedrooms","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"offering","in":"query","required":false,"description":"A home's offering.","schema":{"type":"string","enum":["For sale","For rent","Home swap"]}},{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"description":"Listings, plus total (every listing matching, not just this page), categories (listings per category for the same search, most first), subcategories (with one category asked for, its listings per subcategory), page and pageCount. A listings are from the country asked for, else the signed-in member's own. Each listing carries locationLabel (\"City, ST\", for a person's own listing), locationCountry and sellerIdentityVerified.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListingPage"}}}}}}},"/v1/listings/{id}/visit":{"get":{"tags":["Listings"],"summary":"A shopper's link to a store's own item page (the listing's merchantItemUrl): redirects there (302) and counts a store visit, billed to the store at the store-visit price — once per shopper per listing per day; crawlers and link previews are never counted. Link to this rather than to merchantItemUrl directly. v is the site's own visitor id, when it has one. 404 when the listing has no https store page.\n","security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"v","in":"query","required":false,"schema":{"type":"string","maxLength":100}}],"responses":{"302":{"description":"Redirect to the store's item page"},"404":{"description":"The listing has no store page"}}}},"/v1/directory/storefronts/{slug}":{"get":{"tags":["Directory"],"security":[],"summary":"A member's public storefront: their name, bio, photo, whether they've verified their identity, reputation, completed swaps and general location, and the items they listed themselves (never a store's listings of their items) — public ones only, 24 a page.\n","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","maxLength":60}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"The storefront and a page of its listings","content":{"application/json":{"schema":{"type":"object","properties":{"storefront":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"bio":{"type":["string","null"]},"avatarUrl":{"type":["string","null"],"format":"uri"},"identityVerified":{"type":"boolean"},"reputationScore":{"type":["integer","null"]},"memberSince":{"type":"string","format":"date-time"},"completedSwaps":{"type":"integer"},"location":{"type":["string","null"]}}},"listings":{"type":"array","items":{"$ref":"#/components/schemas/Listing"}},"total":{"type":"integer"},"page":{"type":"integer"},"pageCount":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/directory/listings/{id}":{"get":{"tags":["Directory"],"summary":"Get a single listing from the public global directory","security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Listing"}}}},"404":{"description":"Not found, or not GLOBAL/BOTH visibility"}}}},"/v1/search/listings":{"post":{"tags":["Search"],"summary":"Plain 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.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string"},"category":{"type":"string"},"subcategory":{"type":"string"},"brand":{"type":"string"},"condition":{"type":"string"},"currency":{"type":"string"},"merchantId":{"type":"string","format":"uuid"},"visibility":{"type":"string","enum":["PRIVATE","GLOBAL","BOTH","ENTERPRISE"]},"minTradeValue":{"type":"number"},"maxTradeValue":{"type":"number"},"cursor":{"type":"string"},"limit":{"type":"integer","default":25,"maximum":100}}}}}},"responses":{"200":{"description":"Results","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Listing"}},"nextCursor":{"type":["string","null"]},"total":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-engine/settlement":{"post":{"tags":["TradeEngine"],"summary":"Compute a trade's settlement — each side's value and who owes whom (TradeValue = OriginalPrice + RequestedAmount - WillingToPay per listing). All listings in a trade must share one currency; a trade mixing currencies is rejected.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["listingIdsA","listingIdsB"],"properties":{"listingIdsA":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Existing listing(s) being offered on (side A per the storage convention)."},"listingIdsB":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Listing(s) offered in exchange (side B)."},"extraAmountA":{"type":"number"},"extraAmountB":{"type":"number"}}}}}},"responses":{"201":{"description":"Settlement computed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettlementResult"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/currency/supported":{"get":{"tags":["Currency"],"summary":"List supported currency codes","security":[],"responses":{"200":{"description":"Supported currencies","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}}}},"/v1/currency/convert":{"post":{"tags":["Currency"],"summary":"Convert 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).\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount","from","to"],"properties":{"amount":{"type":"number"},"from":{"type":"string"},"to":{"type":"string"}}}}}},"responses":{"201":{"description":"Converted","content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"number"},"currency":{"type":"string"},"estimateOnly":{"type":"boolean"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/localization/countries":{"get":{"tags":["Localization"],"summary":"List supported countries","security":[],"responses":{"200":{"description":"Countries","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"callingCode":{"type":"string"}}}}}}}}}},"/v1/localization/languages":{"get":{"tags":["Localization"],"summary":"List supported languages","security":[],"responses":{"200":{"description":"Languages","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}}}}}}}}}},"/v1/localization/locate":{"get":{"tags":["Localization"],"security":[],"summary":"Where the caller's connection seems to be: country, and region and city when known. For filling a place in the background; ask the person when city is null. A signed-in member can pass their device's coordinates to get the city there instead.\n","parameters":[{"in":"query","name":"lat","schema":{"type":"number","minimum":-90,"maximum":90},"description":"A signed-in member's latitude. Rounded to about a kilometre before use."},{"in":"query","name":"lng","schema":{"type":"number","minimum":-180,"maximum":180},"description":"A signed-in member's longitude. Rounded to about a kilometre before use."}],"responses":{"200":{"description":"The caller's approximate place","content":{"application/json":{"schema":{"type":"object","properties":{"country":{"type":["string","null"],"description":"ISO 3166-1 alpha-2"},"region":{"type":["string","null"],"description":"State or region code, e.g. TX"},"city":{"type":["string","null"]},"latitude":{"type":["number","null"],"description":"Roughly where, rounded to about a kilometre"},"longitude":{"type":["number","null"],"description":"Roughly where, rounded to about a kilometre"},"source":{"type":["string","null"],"enum":["coordinates","cdn","ip-city","ip-country",null]}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/localization/normalize-phone":{"post":{"tags":["Localization"],"summary":"Normalize a phone number to a canonical format","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["phone"],"properties":{"phone":{"type":"string"}}}}}},"responses":{"201":{"description":"Normalized","content":{"application/json":{"schema":{"type":"object","properties":{"normalized":{"type":"string"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/matching/candidates":{"post":{"tags":["Matching"],"summary":"The best swap candidates for one of your listings, within its visibility (PRIVATE, GLOBAL or BOTH), ranked by Trade Compatibility Score — only listings open to swaps (TRADE or BOTH), in the same currency, from other sellers. A SALE listing has none.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["listingId"],"properties":{"listingId":{"type":"string","format":"uuid"},"limit":{"type":"integer","minimum":1,"maximum":50,"default":20}}}}}},"responses":{"201":{"description":"Ranked candidates","content":{"application/json":{"schema":{"type":"object","properties":{"listingId":{"type":"string","format":"uuid"},"candidates":{"type":"array","items":{"$ref":"#/components/schemas/MatchCandidate"}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/matching/score":{"post":{"tags":["Matching"],"summary":"Direct pairwise Trade Compatibility Score for two known listings — used to preview compatibility before sending an offer","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["listingIdA","listingIdB"],"properties":{"listingIdA":{"type":"string","format":"uuid"},"listingIdB":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Score","content":{"application/json":{"schema":{"type":"object","properties":{"score":{"type":"number"},"breakdown":{"$ref":"#/components/schemas/CompatibilityBreakdown"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/recommendations/sellers/{sellerId}":{"get":{"tags":["Recommendations"],"summary":"Personalized \"for you\" feed for a seller: the best trade candidates across all of the seller's active listings, each shown once.\n","parameters":[{"name":"sellerId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"Recommendations","content":{"application/json":{"schema":{"type":"object","properties":{"sellerId":{"type":"string","format":"uuid"},"recommendations":{"type":"array","items":{"type":"object","properties":{"listingId":{"type":"string","format":"uuid"},"merchantId":{"type":"string","format":"uuid"},"title":{"type":"string"},"category":{"type":"string"},"subcategory":{"type":["string","null"]},"brand":{"type":["string","null"]},"condition":{"type":["string","null"]},"originalPrice":{"type":"number"},"currency":{"type":"string"},"score":{"type":"number"},"matchedAgainstListingId":{"type":"string","format":"uuid","description":"Which of the seller's own listings this candidate scored best against."}}}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/offers":{"post":{"tags":["Offers"],"summary":"Create 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.\n","description":"What's offered must be worth at least half the listed price of what's asked for: the listed prices of the items offered, any cash their listings say their owner will add, extraAmountOffered and cashIncluded all count. Otherwise the offer is refused with 422 OFFER_BELOW_HALF_VALUE, whose details give required, offered and shortfall — include the shortfall as cashIncluded, or offer more. cashIncluded is part of the difference the sender pays, so it can be at most that difference (422 CASH_INCLUDED_TOO_HIGH). An empty listingIdsB is a request to buy a member's item listed for sale, at its listed price.\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOfferInput"}}}},"responses":{"201":{"description":"Offer created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Offer"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"tags":["Offers"],"summary":"List offers for a seller","parameters":[{"name":"sellerId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"direction","in":"query","required":false,"schema":{"type":"string","enum":["sent","received","all"],"default":"all"}}],"responses":{"200":{"description":"Offers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Offer"}}}}}}}},"/v1/offers/quote":{"post":{"tags":["Offers"],"summary":"The trade quote for a proposed offer — what each trader pays, at which store — to show the sender before sending. Pass its fingerprint to POST /v1/offers. problems lists any store that hasn't finished its trade setup; such an offer can't be sent.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["listingIdsA","listingIdsB"],"properties":{"listingIdsA":{"type":"array","items":{"type":"string","format":"uuid"}},"listingIdsB":{"type":"array","items":{"type":"string","format":"uuid"}},"extraAmountOffered":{"type":"number","minimum":0},"cashIncluded":{"type":"number","minimum":0}}}}}},"responses":{"201":{"description":"Quote","content":{"application/json":{"schema":{"type":"object","properties":{"quote":{"$ref":"#/components/schemas/TradeQuote"},"problems":{"type":"array","items":{"$ref":"#/components/schemas/QuoteProblem"}},"tradeable":{"type":"boolean"},"coverage":{"type":"object","description":"What the offer is worth against the half of the asked-for listed price it must reach. shortfall is the cash still to include (0 when enough).\n","properties":{"required":{"type":"number"},"offered":{"type":"number"},"shortfall":{"type":"number"},"enough":{"type":"boolean"}}},"maxCashIncluded":{"type":"number","description":"The most cash that can be included — the difference the sender pays."}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/offers/{id}/quote":{"get":{"tags":["Offers"],"summary":"An offer's trade quote — locked once the offer is accepted, otherwise the current one — and whether each party has confirmed it. If the current quote differs from the one confirmed, both confirmations are cleared.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Quote","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferQuoteView"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/offers/{id}/quote/acknowledge":{"post":{"tags":["Offers"],"summary":"A party confirms they've seen the offer's current quote — needed from the sender again when the quote changed after sending, or after 72 hours. Refused with 409 QUOTE_CHANGED if the fingerprint isn't the current one.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["quoteFingerprint"],"properties":{"actingSellerId":{"type":"string","format":"uuid"},"quoteFingerprint":{"type":"string"}}}}}},"responses":{"201":{"description":"Confirmed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferQuoteView"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/offers/{id}":{"get":{"tags":["Offers"],"summary":"Get an offer. A PENDING offer past its expiresAt is lazily transitioned to EXPIRED on read.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Offer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Offer"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/offers/{id}/accept":{"post":{"tags":["Offers"],"summary":"Accept a PENDING offer — only the recipient (toSellerId) may accept, confirming the quote they were shown (GET /v1/offers/{id}/quote) by its fingerprint. Accepting locks the quote: the trade's checkouts are opened from it. Refused with 409 QUOTE_CHANGED if the quote has changed, or QUOTE_AWAITING_SENDER if the sender's confirmation is for an older quote or more than 72 hours old. The trade session follows shortly after (offer.accepted, then trade.created).\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["actingSellerId","quoteFingerprint"],"properties":{"actingSellerId":{"type":"string","format":"uuid"},"quoteFingerprint":{"type":"string"}}}}}},"responses":{"201":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Offer"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/offers/{id}/reject":{"post":{"tags":["Offers"],"summary":"Reject a PENDING offer — only the recipient (toSellerId) may reject","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["actingSellerId"],"properties":{"actingSellerId":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Offer"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/offers/{id}/cancel":{"post":{"tags":["Offers"],"summary":"Cancel a DRAFT or PENDING offer — only the sender (fromSellerId) may cancel","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["actingSellerId"],"properties":{"actingSellerId":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Offer"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/offers/{id}/counter":{"post":{"tags":["Offers"],"summary":"Counter 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.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CounterOfferInput"}}}},"responses":{"201":{"description":"Counter-offer created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Offer"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions":{"get":{"tags":["TradeSessions"],"summary":"List 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.\n","parameters":[{"name":"sellerId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Trade sessions","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TradeSession"}}}}}}}},"/v1/trade-sessions/records":{"get":{"tags":["TradeSessions"],"summary":"Your trade records, for your books","description":"Every trade your Merchant took part in that started in the date range (default: the last 90 days; at most a year at a time), one row per side you were on. Each row has the items each side gave with their prices as they were when checkout opened, both sides' trade values, any cash added to the offer, what each side's trader paid at checkout and when, and how your side settled. TBBN doesn't calculate or collect tax on a trade: add the tax your own checkout charged from your own records. Needs a role that can view trades.\n","parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Trade records","content":{"application/json":{"schema":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"records":{"type":"array","items":{"$ref":"#/components/schemas/TradeRecord"}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}":{"get":{"tags":["TradeSessions"],"summary":"Get a trade session","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Trade session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeSession"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}/cancel":{"post":{"tags":["TradeSessions"],"summary":"Cancel a trade session — carries no consequence to either party. Only a party to the trade (sellerA or sellerB) may cancel, and only before the session reaches a terminal status, or — on a MERCHANT_CHECKOUT trade — before either checkout has been paid; once one has, the trade can only be closed with close-unreciprocated, after the hold. Releases any RESERVED reservations as a side effect.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["actingSellerId"],"properties":{"actingSellerId":{"type":"string","format":"uuid"},"reason":{"type":"string"}}}}}},"responses":{"201":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeSession"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}/close-unreciprocated":{"post":{"tags":["TradeSessions"],"summary":"Closes a trade where one checkout was paid and the other never was. Each trader checks out with the store of the items they receive, so the unpaid checkout is one trader's items that the other trader never paid for. Either trader may close it — only once the hold has elapsed since the paid checkout (a floor — they may wait longer). `note` is required and is kept on the session; the trade can never be resumed or completed after this. Releases the unpaid checkout's items back to their seller; the paid checkout stands, and that trader receives what they paid for.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["note"],"properties":{"actingSellerId":{"type":"string","format":"uuid"},"note":{"type":"string"}}}}}},"responses":{"201":{"description":"Closed (status CLOSED_UNRECIPROCATED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeSession"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}/excuse-fulfillment":{"post":{"tags":["TradeSessions"],"summary":"A Merchant whose own trade checkout was never paid excuses itself from it, once the hold has elapsed since the other checkout was paid: its items are released back to its seller and the trade closes. The other checkout stands. Merchant-credential only (no actingSellerId — this is a Merchant action, not a Seller one). `note` is required.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["note"],"properties":{"note":{"type":"string"}}}}}},"responses":{"201":{"description":"Closed (status CLOSED_UNRECIPROCATED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeSession"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}/external-settlement":{"post":{"tags":["TradeSessions"],"summary":"Report how a matching-only side settled on your own platform","description":"For a side whose payment row has settlement EXTERNAL (your merchant runs its own checkout — checkoutEnabled false). Called once, by a merchant on that side, after the exchange is settled and fulfilled on your platform (`completed`) or has fallen through (`failed`, with a reason). completed: if the other side has also finished, the trade completes; if the other side still owes TBBN checkout, its counterparty hold starts. failed: this side's reservation is released; if the other side has already paid, the trade closes as CLOSED_UNRECIPROCATED (that side's purchase stands), otherwise the trade is cancelled and every reservation released. A side not reported by its externalSettlementDueAt (14 days) is released as failed automatically, unless its merchant extended the deadline once (…/external-settlement/extend). A report that arrives after the due time but before the side has been released is still accepted. Safe to retry: repeating the outcome already recorded returns the trade unchanged; a different outcome, or a completion after the side was released, is 409. No checkout-orchestration fee is charged for an EXTERNAL side.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["side","outcome"],"properties":{"side":{"type":"string","enum":["A","B"]},"outcome":{"type":"string","enum":["completed","failed"]},"reason":{"type":"string","maxLength":1000,"description":"Required when outcome is failed."}}}}}},"responses":{"201":{"description":"The trade session with its payment rows","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeSession"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}/external-settlement/extend":{"post":{"tags":["TradeSessions"],"summary":"Extend a matching-only side's report deadline once","description":"For a delivery that genuinely needs longer. A merchant on an EXTERNAL side can move its externalSettlementDueAt out once, by 1 to 14 days, with a reason — only before the deadline passes and only while the side is unreported. Sends trade.external_settlement_extended to both sides. A second extension, or one after the deadline, is 409.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["side","days","reason"],"properties":{"side":{"type":"string","enum":["A","B"]},"days":{"type":"integer","minimum":1,"maximum":14},"reason":{"type":"string","maxLength":1000}}}}}},"responses":{"201":{"description":"The trade session with its payment rows","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeSession"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}/acknowledge-hold-terms":{"post":{"tags":["TradeSessions"],"summary":"Records that the caller's side was shown and agreed to this trade's real hold terms (see GET :id's `holdTerms` field) before completing checkout on a MERCHANT_CHECKOUT trade.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"actingSellerId":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeSession"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}/scheduling/confirm":{"post":{"tags":["TradeSessions"],"summary":"Peer-to-peer trade completion — either party confirms the exchange happened on their side. Once both sides have confirmed, the session moves to COMPLETED.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["actingSellerId"],"properties":{"actingSellerId":{"type":"string","format":"uuid"},"confirmedByMerchantUserId":{"type":"string","format":"uuid","description":"Required only when actingSellerId is a headless Seller (no linked TBBN account) — identifies the Merchant rep confirming on that Seller's behalf.\n"}}}}}},"responses":{"200":{"description":"Confirmed (or COMPLETED, if both sides have now confirmed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeSession"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}/scheduling/fail":{"post":{"tags":["TradeSessions"],"summary":"Either party reports the peer-to-peer exchange failed (no-show, item mismatch)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["actingSellerId"],"properties":{"actingSellerId":{"type":"string","format":"uuid"},"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"Moved to EXCHANGE_FAILED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeSession"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}/space-proposals":{"post":{"tags":["TradeSessions"],"summary":"Propose a Space to meet at for a peer-to-peer trade","description":"Proposes a meeting place, replacing any open proposal. With `spaceId` and `acceptTerms: true`, the proposer agrees to pay for the Space and to its booking terms: when the other trader accepts, the Space is booked in the proposer's name and they pay on the booking's payment page. A `branchId` alone proposes a location only.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"spaceId":{"type":"string","format":"uuid"},"branchId":{"type":"string","format":"uuid"},"scheduledAt":{"type":"string","format":"date-time"},"acceptTerms":{"type":"boolean"},"actingSellerId":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Proposed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeSpaceProposal"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"tags":["TradeSessions"],"summary":"List a trade's meeting-place proposals, newest first","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Proposals","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TradeSpaceProposal"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}/space-proposals/{proposalId}/decline":{"post":{"tags":["TradeSessions"],"summary":"Decline the other trader's meeting-place proposal","description":"Only the trader who didn't propose it can decline. Nothing is booked or charged, and either trader can then propose somewhere else.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"proposalId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"actingSellerId":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Declined","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeSpaceProposal"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/trade-sessions/{id}/space-proposals/{proposalId}/accept":{"post":{"tags":["TradeSessions"],"summary":"Accept the other trader's meeting-place proposal","description":"Only the trader who didn't propose it can accept. For a Space proposal, accepting books the Space in the proposer's name; `booking` is that booking (status, amount and id for the payment page). For a location-only proposal, `booking` is null.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"proposalId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"actingSellerId":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Accepted","content":{"application/json":{"schema":{"type":"object","properties":{"proposal":{"$ref":"#/components/schemas/TradeSpaceProposal"},"booking":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"},"scheduledAt":{"type":["string","null"],"format":"date-time"},"chargeAmount":{"type":["number","null"]},"currency":{"type":["string","null"]}}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/checkout/webhooks/payment":{"post":{"tags":["Checkout"],"summary":"Merchant-facing payment status callback (mirrors the payment.completed/payment.failed webhook catalog entries). `side` is your side — the side whose items your checkout sells; the trader paying there is the other side's, receiving those items. A completed payment clears your side to ship them immediately, independent of the other checkout. If this is the first checkout paid, starts the counterparty hold (trade.counterparty_hold_started) rather than waiting on the other checkout at all. Rejected once the trade session has reached a terminal status (including CLOSED_UNRECIPROCATED) — a closed trade can never be paid into again.\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tradeSessionId","side","status"],"properties":{"tradeSessionId":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["A","B"]},"status":{"type":"string","enum":["completed","failed"]},"merchantCheckoutSessionRef":{"type":"string"},"failedReason":{"type":"string"}}}}}},"responses":{"201":{"description":"Recorded"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/checkout/webhooks/fulfillment":{"post":{"tags":["Checkout"],"summary":"Merchant-facing fulfillment status callback, per-side: `started` requires only that this same `side`'s own payment is COMPLETE, regardless of the other side. The whole trade session auto-completes once both sides report `completed` — no separate step needed. Rejected once the session has reached a terminal status.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tradeSessionId","side","status"],"properties":{"tradeSessionId":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["A","B"]},"status":{"type":"string","enum":["started","completed"]}}}}}},"responses":{"201":{"description":"Recorded"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/checkout/payments":{"get":{"tags":["Checkout"],"summary":"Get both sides' payment records for a trade session","parameters":[{"name":"tradeSessionId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Payments","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TradeSessionPayment"}}}}}}}},"/v1/conversations":{"get":{"security":[{"BearerAuth":[]}],"tags":["Messages"],"summary":"Your conversations about members' listings — items you asked about, and your own items people asked you about — newest first.\n","responses":{"200":{"description":"Conversations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ListingConversationSummary"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/inbox":{"get":{"security":[{"BearerAuth":[]}],"tags":["Messages"],"summary":"Every conversation you're in — about items, on your trades, and with Spaces, as a guest or a host — newest first, with what's unread in each.\n","responses":{"200":{"description":"Conversations","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InboxThread"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/inbox/summary":{"get":{"security":[{"BearerAuth":[]}],"tags":["Messages"],"summary":"How many messages you haven't read, in total and by kind","responses":{"200":{"description":"Unread messages","content":{"application/json":{"schema":{"type":"object","properties":{"unread":{"type":"integer"},"unreadThreads":{"type":"integer"},"items":{"type":"integer"},"trades":{"type":"integer"},"spaces":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/conversations/unread-count":{"get":{"security":[{"BearerAuth":[]}],"tags":["Messages"],"summary":"How many of your conversations have messages you haven't read","responses":{"200":{"description":"Count","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/conversations/listings/{listingId}":{"get":{"security":[{"BearerAuth":[]}],"tags":["Messages"],"summary":"Your conversation about a listing, if you've started one","parameters":[{"name":"listingId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The conversation's id, or null","content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":["string","null"],"format":"uuid"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"post":{"security":[{"BearerAuth":[]}],"tags":["Messages"],"summary":"Ask the person who listed an item about it. Carries on your conversation about it if you have one. A member's own listing only — not a store's.\n","description":"Messages can't include phone numbers, emails or social handles, or arrange paying outside TBBN (422 CONTACT_DETAILS, OFF_PLATFORM_PAYMENT); once a swap is agreed, the trade's own messages take over.\n","parameters":[{"name":"listingId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body"],"properties":{"body":{"type":"string","maxLength":2000}}}}}},"responses":{"201":{"description":"Sent","content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","format":"uuid"},"message":{"$ref":"#/components/schemas/ListingConversationMessage"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/conversations/{id}":{"get":{"security":[{"BearerAuth":[]}],"tags":["Messages"],"summary":"A conversation and its messages, oldest first. Reading it marks it read for you.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Conversation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListingConversation"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/conversations/{id}/messages":{"post":{"security":[{"BearerAuth":[]}],"tags":["Messages"],"summary":"Reply in a conversation about a listing","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body"],"properties":{"body":{"type":"string","maxLength":2000}}}}}},"responses":{"201":{"description":"Sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListingConversationMessage"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/messages":{"post":{"security":[{"BearerAuth":[]}],"tags":["Messages"],"summary":"Send a message on a trade — the sender is derived from the caller's own session","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tradeSessionId","body"],"properties":{"tradeSessionId":{"type":"string","format":"uuid"},"body":{"type":"string","maxLength":2000}}}}}},"responses":{"201":{"description":"Message sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeMessage"}}}},"422":{"description":"Rejected — the message body appears to contain contact/off-platform information"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/messages/{tradeSessionId}":{"get":{"security":[{"BearerAuth":[]}],"tags":["Messages"],"summary":"List a trade's visible messages, oldest first — caller must be one of its two sellers","parameters":[{"name":"tradeSessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Messages","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TradeMessage"}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/messages/{id}/report":{"post":{"security":[{"BearerAuth":[]}],"tags":["Messages"],"summary":"Report a message. It is hidden while TBBN reviews it. Caller must be a party to the message's trade.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","maxLength":500}}}}}},"responses":{"201":{"description":"Reported"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/billing/subscriptions":{"post":{"security":[{"BearerAuth":[]}],"tags":["Billing"],"summary":"Create a Business's SaaS subscription","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["businessId","tier"],"properties":{"businessId":{"type":"string","format":"uuid"},"billingEmail":{"type":"string","format":"email","description":"Where receipts go. Omit it (or send \"\") to use your account's email."},"tier":{"type":"string","enum":["SANDBOX","STARTER","GROWTH","ENTERPRISE","PAY_AS_YOU_GO"],"description":"PAY_AS_YOU_GO creates no recurring Stripe subscription — save the card charged weekly next."}}}}}},"responses":{"201":{"description":"Subscription created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessSubscription"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/billing/subscriptions/{businessId}":{"get":{"security":[{"BearerAuth":[]}],"tags":["Billing"],"summary":"Get a Business's current active SaaS subscription","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessSubscription"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/billing/subscriptions/{businessId}/change-tier":{"post":{"security":[{"BearerAuth":[]}],"tags":["Billing"],"summary":"Change a Business's subscription tier","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tier"],"properties":{"tier":{"type":"string","enum":["SANDBOX","STARTER","GROWTH","ENTERPRISE","PAY_AS_YOU_GO"]}}}}}},"responses":{"201":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessSubscription"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/billing/subscriptions/{businessId}/cancel":{"post":{"security":[{"BearerAuth":[]}],"tags":["Billing"],"summary":"Cancel a Business's subscription","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"201":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessSubscription"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/billing/subscriptions/{businessId}/confirm-payment-method":{"post":{"security":[{"BearerAuth":[]}],"tags":["Billing"],"summary":"Pay-As-You-Go card capture, step 2 — save the payment method Stripe.js resolved the SetupIntent to","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["stripePaymentMethodId"],"properties":{"stripePaymentMethodId":{"type":"string"}}}}}},"responses":{"201":{"description":"Saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessSubscription"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/billing/pricing/{businessId}":{"get":{"security":[{"BearerAuth":[]}],"tags":["Billing"],"summary":"What TBBN charges this Business, in its billing currency","description":"Plan prices, usage rates, top-up limits and fees. A Business is billed in its country's currency where TBBN has set prices there, otherwise USD, and keeps that currency once it has been charged (currencyFixed). Percent prices are percentages.\n","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The Business's prices","content":{"application/json":{"schema":{"type":"object","properties":{"country":{"type":["string","null"]},"currency":{"type":"string","description":"ISO 4217"},"local":{"type":"boolean"},"currencyFixed":{"type":"boolean"},"prices":{"type":"object","additionalProperties":{"type":"number"}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/billing/merchant/summary":{"get":{"tags":["Billing"],"summary":"Your Merchant's plan — its Business's — and this period's usage: for each service, your own count (rawCount), your Business's total across its Merchants (businessCount, what the allowance is measured against) and the plan's allowance (included; null means unlimited). Works with an API key or a merchant dashboard session whose role can see billing or analytics. Without a paid plan, the tier is SANDBOX and the period is the calendar month.\n","responses":{"200":{"description":"Plan and usage","content":{"application/json":{"schema":{"type":"object","properties":{"merchantId":{"type":"string","format":"uuid"},"businessId":{"type":["string","null"],"format":"uuid"},"businessName":{"type":["string","null"]},"currency":{"type":"string"},"tier":{"type":"string","enum":["SANDBOX","STARTER","GROWTH","PAY_AS_YOU_GO","ENTERPRISE"]},"billingInterval":{"type":["string","null"],"enum":["MONTH","YEAR",null]},"status":{"type":["string","null"]},"basePriceUsd":{"type":["number","null"]},"windowStart":{"type":"string","format":"date-time"},"windowEnd":{"type":"string","format":"date-time"},"lineItems":{"type":"array","items":{"type":"object","properties":{"eventType":{"type":"string"},"rawCount":{"type":"integer"},"businessCount":{"type":"integer"},"included":{"type":["integer","null"]},"overageCost":{"type":"number"}}}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/billing/usage/{businessId}":{"get":{"security":[{"BearerAuth":[]}],"tags":["Billing"],"summary":"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 the Pricing reference.\n","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"billingPeriodRef","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Usage summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageSummary"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/billing/invoices/{businessId}":{"get":{"security":[{"BearerAuth":[]}],"tags":["Billing"],"summary":"A Business's invoices","description":"Every Stripe invoice billed to the Business (plan and Pay-As-You-Go usage), newest first, with its status (draft, open, paid, uncollectible, void), amounts in its currency and links to the hosted invoice and PDF. Also returns totals per status and currency. For anyone on the Business's team who can see billing.\n","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["draft","open","paid","uncollectible","void"]}},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["PLAN","PAYG","MEMBERSHIP","OTHER"]}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200}}],"responses":{"200":{"description":"Invoices","nextCursor and summary":null},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/billing/statements/{businessId}":{"get":{"security":[{"BearerAuth":[]}],"tags":["Billing"],"summary":"Consolidated Business billing statement — merchantPlan (owed) and spaceEarnings (already paid out, net of commission) as two separate sections, never merged into one number.\n","parameters":[{"name":"businessId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"billingPeriodRef","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Statement","content":{"application/json":{"schema":{"type":"object","properties":{"businessId":{"type":"string","format":"uuid"},"merchantPlan":{"$ref":"#/components/schemas/UsageSummary"},"spaceEarnings":{"type":"object","properties":{"currency":{"type":"string"},"totalPaidOut":{"type":"number"},"byBranch":{"type":"array","items":{"type":"object","properties":{"branchId":{"type":"string","format":"uuid"},"branchName":{"type":"string"},"totalPaidOut":{"type":"number"},"payoutCount":{"type":"integer"}}}}}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/notifications":{"get":{"security":[{"BearerAuth":[]}],"tags":["Notifications"],"summary":"Your notifications, newest first — what happened and where it opens","responses":{"200":{"description":"Notifications","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}}}}}}}},"/v1/notifications/unread-count":{"get":{"security":[{"BearerAuth":[]}],"tags":["Notifications"],"summary":"How many of your notifications are unread","responses":{"200":{"description":"Count","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/notifications/read-all":{"post":{"security":[{"BearerAuth":[]}],"tags":["Notifications"],"summary":"Mark all your notifications read","responses":{"201":{"description":"How many were marked read","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/notifications/{id}/read":{"post":{"security":[{"BearerAuth":[]}],"tags":["Notifications"],"summary":"Mark a notification read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Marked read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Notification"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/notifications/unsubscribe":{"get":{"tags":["Notifications"],"summary":"Public unsubscribe-center status (no auth) — pre-fills which categories an email is already opted out of. `token` (from an emailed unsubscribe link) only improves this prefill UX; without a valid one the form just shows nothing pre-ticked, since unsubscribe itself never requires one.\n","security":[],"parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string","format":"email"}},{"name":"token","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current opt-out status","content":{"application/json":{"schema":{"type":"object","properties":{"optedOutCategories":{"type":"array","items":{"type":"string","enum":["ACCOUNT","TRADE_ACTIVITY","SPACE_BOOKINGS","BILLING_PAYOUTS","PRODUCT_NEWS"]}}}}}}}}},"post":{"tags":["Notifications"],"summary":"Opt an email out of the given categories (no auth — this is how every public unsubscribe link works, logged in or not). SECURITY mail can never be opted out of. Submitting an empty array re-subscribes to everything.\n","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","categories"],"properties":{"email":{"type":"string","format":"email"},"categories":{"type":"array","items":{"type":"string","enum":["ACCOUNT","TRADE_ACTIVITY","SPACE_BOOKINGS","BILLING_PAYOUTS","PRODUCT_NEWS"]}}}}}}},"responses":{"201":{"description":"Preferences saved"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/notifications/preferences":{"get":{"security":[{"BearerAuth":[]}],"tags":["Notifications"],"summary":"The authenticated, stronger override the public unsubscribe form doesn't expose — the caller's own email is resolved server-side from their principal, never client-supplied.\n","responses":{"200":{"description":"Current preferences","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":["string","null"]},"allNonEssentialEmailOptOut":{"type":"boolean"},"optedOutCategories":{"type":"array","items":{"type":"string","enum":["ACCOUNT","TRADE_ACTIVITY","SPACE_BOOKINGS","BILLING_PAYOUTS","PRODUCT_NEWS"]}}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"patch":{"security":[{"BearerAuth":[]}],"tags":["Notifications"],"summary":"Set the authenticated caller's own category opt-outs, plus the allNonEssentialEmailOptOut master switch (checked first, ahead of any per-category opt-out, and automatically covers a category added in the future).\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["categories","allNonEssentialEmailOptOut"],"properties":{"categories":{"type":"array","items":{"type":"string","enum":["ACCOUNT","TRADE_ACTIVITY","SPACE_BOOKINGS","BILLING_PAYOUTS","PRODUCT_NEWS"]}},"allNonEssentialEmailOptOut":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Preferences saved"},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/webhooks/subscriptions":{"post":{"tags":["Webhooks"],"summary":"Subscribe to events. events is an array of event-type strings (e.g. offer.accepted) or [\"*\"] for every event. See the Events & webhook catalog guide for the full list. The signing secret is returned with the subscription. The url must be a public https address.\n","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["merchantId","url","events"],"properties":{"merchantId":{"type":"string","format":"uuid"},"name":{"type":"string","maxLength":80,"description":"Your own name, e.g. Order system."},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"},"minItems":1}}}}}},"responses":{"201":{"description":"Subscribed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscription"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"tags":["Webhooks"],"summary":"List a merchant's webhook subscriptions","parameters":[{"name":"merchantId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Subscriptions","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}}}},"/v1/webhooks/subscriptions/{id}":{"patch":{"tags":["Webhooks"],"summary":"Rename a webhook subscription, move it to a new address or change its events. The signing secret is kept, so your receiver needs no change.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":80},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"},"minItems":1}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscription"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/webhooks/subscriptions/{id}/disable":{"post":{"tags":["Webhooks"],"summary":"Disable a webhook subscription — no request body; ownership is derived from the subscription itself plus the calling merchant's own credential, not a body claim.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"201":{"description":"Disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscription"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/webhooks/deliveries":{"get":{"tags":["Webhooks"],"summary":"List webhook deliveries for one of your subscriptions. subscriptionId is required.\n","parameters":[{"name":"subscriptionId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deliveries","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}}}}}}}},"/v1/webhooks/deliveries/{id}/replay":{"post":{"tags":["Webhooks"],"summary":"Replay 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.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"201":{"description":"Replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDelivery"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/webhooks/business-subscriptions":{"post":{"security":[{"BearerAuth":[]}],"tags":["Webhooks"],"summary":"Register a Business webhook — fires on Space booking lifecycle events (space_booking.created/consent_required/consented/paid/cancelled/completed/no_show) and, with includeMerchantEvents, on the trade, offer, listing and checkout events of every Merchant the Business runs. Requires the Owner, an Admin or a Developer on the Business. The url must be a public https address.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["businessId","url","events"],"properties":{"businessId":{"type":"string","format":"uuid"},"name":{"type":"string","maxLength":80,"description":"Your own name, e.g. Front desk."},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"includeMerchantEvents":{"type":"boolean","default":false}}}}}},"responses":{"201":{"description":"Subscription created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessWebhookSubscription"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"security":[{"BearerAuth":[]}],"tags":["Webhooks"],"summary":"List a Business's webhook subscriptions","parameters":[{"name":"businessId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Subscriptions","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BusinessWebhookSubscription"}}}}}}}},"/v1/webhooks/business-subscriptions/{id}":{"patch":{"security":[{"BearerAuth":[]}],"tags":["Webhooks"],"summary":"Rename a Business webhook, move it to a new address, change its events or whether it carries your Merchants' events. The signing secret is kept.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":80},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"},"minItems":1},"includeMerchantEvents":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessWebhookSubscription"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/webhooks/business-subscriptions/{id}/disable":{"post":{"security":[{"BearerAuth":[]}],"tags":["Webhooks"],"summary":"Disable a Business webhook subscription — no request body; ownership is derived from the subscription itself plus the caller's own session, not a body claim.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessWebhookSubscription"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/webhooks/business-deliveries":{"get":{"security":[{"BearerAuth":[]}],"tags":["Webhooks"],"summary":"List Business webhook deliveries for one of your subscriptions. subscriptionId is required.\n","parameters":[{"name":"subscriptionId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deliveries","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BusinessWebhookDelivery"}}}}}}}},"/v1/webhooks/business-deliveries/{id}/replay":{"post":{"security":[{"BearerAuth":[]}],"tags":["Webhooks"],"summary":"Replay a Business webhook delivery","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"201":{"description":"Replayed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessWebhookDelivery"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/reputation/sellers/{id}":{"get":{"tags":["Reputation"],"summary":"Get a seller's reputation score (0-100). A new seller starts at the neutral baseline of 50.\n","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Score","content":{"application/json":{"schema":{"type":"object","properties":{"sellerId":{"type":"string","format":"uuid"},"score":{"type":"integer"}}}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/analytics/overview":{"get":{"tags":["Analytics"],"summary":"GMV, trade volume, offer-acceptance match rate, and completion rate over a date range (default: trailing 30 days) for your own merchant — merchantId, if supplied, must match the caller.\n","parameters":[{"name":"merchantId","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Overview","content":{"application/json":{"schema":{"type":"object","properties":{"range":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}}},"merchantId":{"type":["string","null"],"format":"uuid"},"gmv":{"type":"number"},"tradeVolume":{"type":"integer"},"completedTrades":{"type":"integer"},"completionRate":{"type":"number","description":"Percentage, 0-100."},"offersCreated":{"type":"integer"},"offersAccepted":{"type":"integer"},"matchRate":{"type":"number","description":"Percentage, 0-100 — interpreted as offer-acceptance rate; there is no separate persisted \"match\" record.\n"}}}}}}}}},"/v1/sandbox/test-account":{"post":{"security":[{"BearerAuth":[]}],"tags":["Sandbox"],"summary":"Switch on test mode for the signed-in rep's Merchant","description":"Creates the Merchant's test account the first time — with two test sellers — and returns it every time after. Test keys (sk_sandbox_) are then created with POST /v1/api-keys in test mode, and work only on the test account: its data is never public, never trades with live listings and isn't billed. Needs a role that can manage test keys.\n","responses":{"200":{"description":"The test account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestAccount"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}},"get":{"security":[{"BearerAuth":[]}],"tags":["Sandbox"],"summary":"The signed-in rep's test account","description":"testMerchantId is null until test mode has been switched on.","responses":{"200":{"description":"The test account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestAccount"}}}},"default":{"$ref":"#/components/responses/ErrorResponse"}}}},"/v1/sandbox/fixtures":{"get":{"tags":["Sandbox"],"summary":"Canned 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","security":[],"responses":{"200":{"description":"Fixtures","content":{"application/json":{"schema":{"type":"object"}}}}}}}}}