Behio Storefront SDK
Advanced

B2B Features

Quote requests, cookie consent, and return requests

Quote Requests

Allow B2B customers to request custom pricing. The contact email is the ownership gate for reading and accepting a quote (quotes carry contact PII and negotiated prices), so keep it available on the confirmation page.

const { data: quote, error } = await client.quotes.submit({
  contactName: 'Jan Novak',
  contactEmail: '[email protected]',
  contactPhone: '+420123456789',
  companyName: 'ACME Corp',
  message: 'We need bulk pricing for Q3',
  items: [
    { productId: 'product-1', quantity: 100, requestedPrice: 80 },
    { productId: 'product-2', quantity: 50 },
  ],
});
// quote.status: PENDING | QUOTED | ACCEPTED | REJECTED | EXPIRED

Check Status

const { data: status } = await client.quotes.getStatus('quote-id', '[email protected]');
// { id, status, quotedTotal, quotedCurrency, quotedNote, expiresAt, items, ... }

Accept Quote

Only a quote in QUOTED status can be accepted; accepting twice is idempotent and an expired quote flips to EXPIRED:

const { data: accepted } = await client.quotes.accept('quote-id', '[email protected]');

Return Requests (EU Withdrawal Button)

EU Directive 2023/2673 (effective 19 June 2026) requires an easy, clearly labelled online way to withdraw from the contract. Build a public /returns page with this three-step flow. No customer login is needed; the email used on the order is the ownership gate.

1. Look Up the Order

The customer enters their order number and email; you get back the internal ids and per-item returnable quantities:

const { data: order, error } = await client.returns.lookupOrder(
  '2026-0042',
  '[email protected]',
);
// order.items: [{ orderItemId, productName, quantity, returnableQuantity }]

Cap each quantity input at returnableQuantity and skip items where it is 0 (already claimed by an active return).

2. Submit the Withdrawal

const { data: returnRequest, error } = await client.returns.submit({
  orderId: order.orderId,
  email: '[email protected]', // must match the order email
  reason: 'CHANGED_MIND',      // the 14-day EU default; see all reasons below
  customerNote: 'Optional note',
  items: [
    { orderItemId: 'item-id', productName: 'Product name', quantity: 1 },
  ],
});

Reasons: CHANGED_MIND, DEFECTIVE, WRONG_ITEM, NOT_AS_DESCRIBED, TOO_LATE, DUPLICATE_ORDER, OTHER. The platform immediately sends the acknowledgement email the directive requires; show returnRequest.id to the customer as their reference number.

3. Check Return Status

const { data: status, error } = await client.returns.getStatus(
  'return-id',
  '[email protected]',
);
// status.status: REQUESTED | APPROVED | SHIPPED_BACK | RECEIVED |
//                REFUNDED | REJECTED | CLOSED
// also: refundAmount, refundedAt, returnTrackingNumber, items

Run all three calls from Server Actions (or your backend) so the customer's email never appears in a URL.

The consent log is append-only (GDPR audit trail); get returns the latest active consent and resolves to null when none was recorded yet.

// Record consent (visitorId = your anonymous cookie/fingerprint id, 8-128 chars)
await client.consent.record({
  visitorId: 'visitor-uuid',
  analytics: true,
  marketing: false,
  preferences: true,
});

// Get the latest active consent — data is null when none exists
const { data: consent } = await client.consent.get('visitor-uuid');

// Revoke (idempotent)
await client.consent.revoke('visitor-uuid');

On this page