> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tybritelabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# B2B (Wholesale)

> Build a wholesale buyer experience with the Tybrite SDK — request quotes, accept them into purchase orders, and pay invoices on terms.

The `B2BService` class (accessed via `client.b2B`) is the buyer-facing entry point for **wholesale (B2B) commerce**: a business buyer requests a quote, accepts it into a purchase order, tracks fulfilment, and pays invoices on terms.

Every method here acts on the **signed-in buyer's own records**. Supplier-side work — pricing a quote, confirming a purchase order, recording shipments, and setting credit — happens in the supplier's admin, not through this API. B2B endpoints are available only on a supplier deployment.

***

## Before you start: how wholesale works in Tybrite

A wholesale order moves through a clear sequence, and each step maps to one or two methods here.

* **Request for quote (RFQ).** The buyer asks a supplier to price a set of items and quantities — `createRfq`. The supplier prices it into a quote.
* **Quote.** A priced offer the buyer can accept or reject — `getQuote`, `acceptQuote`, `rejectQuote`. A quote may carry a validity date, after which it expires.
* **Purchase order (PO).** Accepting a quote creates a purchase order — the buyer's commitment. It carries the buyer's payment terms (prepaid, or net 15 / 30 / 60 days). Track it with `getPurchaseOrder` / `listPurchaseOrders`.
* **Invoice.** When the supplier confirms the purchase order, a terms invoice is issued. The buyer sees it with `getInvoice` / `listInvoices` and settles it with `payInvoice` — in full or in parts. An invoice past its due date is marked overdue.

For routine reorders, an approved buyer does not need the full request-for-quote round trip. `createDirectOrder` places an order in one call at the buyer's own agreed prices — the self-serve equivalent of the flow above. The request-for-quote path remains for negotiated or bespoke orders.

<Note>
  Every method requires the API key **plus the buyer's session** — either an `xAuthToken` (a session token from `client.authentication.login`) **or** an `xExternalAuth` assertion if you run your own identity provider. Provide one of the two on every call.

  All create calls (`createRfq`, `createDirectOrder`, `payInvoice`) require an **`idempotencyKey`** — a unique value per action, so a retried request is never applied twice.
</Note>

***

## The typical integration flow

```ts theme={null}
import { Tybrite } from "@tybrite/sdk";

const client = new Tybrite({ TOKEN: "tybrite_pk_...", BASE: "https://api.tybritelabs.com" });
const session = "..."; // the buyer's x-auth-token from client.authentication.login

// 1. Request a quote for some items.
const rfq = await client.b2B.createRfq({
  xAuthToken: session,
  idempotencyKey: crypto.randomUUID(),
  requestBody: {
    line_items: [{ variant_id: "ab8411a6-...", quantity: 25 }],
    note: "Need a wholesale quote",
  },
});

// 2. The supplier prices + sends a quote; the buyer reviews and accepts it.
const quote = await client.b2B.getQuote({ id: quoteId, xAuthToken: session });
const po = await client.b2B.acceptQuote({ id: quoteId, xAuthToken: session });

// 3. Once the supplier confirms the PO, an invoice is issued. Pay it (in full or in part).
const invoices = await client.b2B.listInvoices({ xAuthToken: session });
await client.b2B.payInvoice({
  id: invoices.data[0].id,
  xAuthToken: session,
  idempotencyKey: crypto.randomUUID(),
  requestBody: { amount: 200, payment_method: "bank" },
});
```

***

## Direct checkout

### `createDirectOrder(options)`

Places a wholesale order for an approved buyer in one call, without the request-for-quote round trip — the self-serve path for routine reorders at the buyer's own agreed prices.

Send only the items and quantities. Galactic Core prices every line itself against the buyer's wholesale price list (their per-buyer price, their group's price, or the store default), enforces each item's minimum order quantity and pack size, and computes the totals. **Any price sent by the client is ignored.** An item with no wholesale price for this buyer, a quantity below its minimum order quantity, or a quantity that is not a whole multiple of its pack size, is rejected with a clear reason.

Tax is resolved on the server too. When you include a structured shipping address, tax is calculated accurately for the destination; otherwise the store's configured rate applies, and a tax-exempt buyer is charged no tax. The response's `tax_amount` is the resolved tax, and `total` / `amount` are tax-inclusive.

How the order settles depends on the supplier's policy for this buyer:

* **On terms** — the order is confirmed and a terms invoice is issued (subject to the buyer's credit limit). The response's `settlement` is `"terms"`, with `invoice_id`, `invoice_number`, and `due_date`. Settle it later with `payInvoice`.

* **Pay now** — the order is created awaiting payment. The response's `settlement` is `"pay_now"`, with an `order_id` and `amount`. Complete the payment through the payment flow (`client.payments.initializePayment` then `verifyPayment`) using that `order_id`, exactly as for a normal online order.

* `requestBody.items` — an array of `{ variant_id, quantity }` (required, non-empty). Do not send prices.

* `requestBody.note` — an optional note on the order.

* `requestBody.shipping_address` — an optional free-text shipping address.

* `requestBody.shipping_address_parts` — an optional structured shipping address (`line1`, `city`, `region`, `country`, `postal_code`). Supply it so tax is calculated for the destination.

* `requestBody.currency` — the order currency (e.g. `"USD"`).

* `idempotencyKey` — required.

* `xTimestamp` / `xSignature` — required. A direct order must be signed, like a standard order: `xTimestamp` is the current Unix time in milliseconds, and `xSignature` is the Base64 HMAC-SHA256 of `` `${xTimestamp}.${rawJsonBody}` `` using the store's signing secret. See [Signing requests](/authentication#signing-requests).

```ts theme={null}
const order = await client.b2B.createDirectOrder({
  xAuthToken: session,
  idempotencyKey: crypto.randomUUID(),
  requestBody: {
    items: [{ variant_id: "ab8411a6-...", quantity: 10 }],
  },
});

if (order.data.settlement === "terms") {
  // A terms invoice was issued — pay it later.
  console.log(order.data.invoice_number, "due", order.data.due_date);
} else {
  // Pay now — complete payment against the returned order_id.
  await client.payments.initializePayment({
    requestBody: { provider: "stripe", order_id: order.data.order_id, amount: order.data.amount },
    // ...signing headers as for any payment
  });
}
```

```json Response (terms) theme={null}
{
  "data": {
    "settlement": "terms",
    "purchase_order_id": "5a566f3d-1798-461a-926f-4c38602965ce",
    "po_number": "PO-20260711-ce9be3",
    "invoice_id": "f5199e4a-2f22-4970-a536-bb40068c4518",
    "invoice_number": "INV-20260711-29fad5",
    "due_date": "2026-08-10",
    "total": 100,
    "tax_amount": 0,
    "currency": "USD"
  }
}
```

```json Response (pay now, tax calculated for the destination) theme={null}
{
  "data": {
    "settlement": "pay_now",
    "purchase_order_id": "80338da9-1c05-4d58-a6d0-8cf1b83c5eeb",
    "po_number": "PO-20260711-b599a5",
    "order_id": "aa50d584-7028-49dc-8b06-2a23551ebd47",
    "order_number": "B2B-20260711-933fe9",
    "amount": 59.88,
    "tax_amount": 9.98,
    "currency": "GBP"
  }
}
```

***

## Requests for quote

### `createRfq(options)`

Requests a quote for a set of items and quantities. The supplier prices it into a quote you can later accept.

* `requestBody.line_items` — an array of `{ variant_id, quantity, note? }` (required, non-empty).
* `requestBody.note` — an optional message to the supplier.
* `requestBody.expires_at` — an optional expiry for the request.
* `idempotencyKey` — required.

Returns the created RFQ (`{ data: B2bRfq }`).

### `listRfqs(options)`

Returns your own requests for quote, newest first (`{ data: B2bRfq[] }`).

### `getRfq(options)`

Returns one of your requests for quote by `id`.

***

## Quotes

### `getQuote(options)`

Returns a quote addressed to you by `id`, including its priced line items, totals, and validity date.

### `acceptQuote(options)`

Accepts a sent quote and creates a purchase order from it. The purchase order then awaits the supplier's confirmation before an invoice is issued. Returns `{ data: { quote_id, purchase_order_id, po_number } }`.

### `rejectQuote(options)`

Rejects a quote by `id`.

***

## Purchase orders

### `listPurchaseOrders(options)`

Returns your own purchase orders, newest first (`{ data: B2bPurchaseOrder[] }`).

### `getPurchaseOrder(options)`

Returns one of your purchase orders by `id`, including its status (`issued`, `confirmed`, `partially_fulfilled`, `fulfilled`, `cancelled`), line items, totals, and payment terms.

***

## Invoices

### `listInvoices(options)`

Returns your own invoices, newest first (`{ data: B2bInvoice[] }`). Each carries its status (`issued`, `partially_paid`, `paid`, `overdue`, `void`), amount, amount paid, and due date.

### `getInvoice(options)`

Returns one of your invoices by `id`.

### `payInvoice(options)`

Records a payment against an invoice. Payments settle the outstanding balance and never exceed it — a partial payment is fine; the invoice stays `partially_paid` until fully settled.

* `id` — the invoice id.
* `requestBody.amount` — the amount to pay (must not exceed the balance).
* `requestBody.payment_method` — e.g. `"bank"` or `"cash"`.
* `idempotencyKey` — required (the payment is keyed on it, so a retry with the same key is safe).

Returns `{ data: { payment_id, amount_paid, invoice_status } }`.
