headless.biddevelopers
v1Sandbox console
Sandbox available · Production unavailable

Connect with a short-lived capability

Provision from a trusted Node 22 backend, mint one subject-bound capability in your BFF, then connect the browser directly to the Lot session.

Sandbox endpoints

Minimal WebSocket transport

Install the exact private release-candidate version authorized for your pilot. Pass both values returned by sessionSubprotocols unchanged; the server negotiates only ledgero.v2 and never reflects the proof-bearing capability protocol.

import {
  SessionClient,
  sessionSubprotocols,
  type ClientFrame,
  type ServerFrame,
  type SessionTransport,
} from '@headless-bid/client'

const lotId = provisioned.lotId
const sessionUrl =
  `wss://data.sandbox.headless.bid/v1/lots/${encodeURIComponent(lotId)}/session`

class BrowserWebSocketTransport implements SessionTransport {
  #socket: WebSocket | null = null

  open({ capability, onFrame, onClose }: Parameters<SessionTransport['open']>[0]) {
    const socket = new WebSocket(sessionUrl, [...sessionSubprotocols(capability)])
    this.#socket = socket
    socket.addEventListener('message', (event) => {
      onFrame(JSON.parse(String(event.data)) as ServerFrame)
    })
    socket.addEventListener('close', (event) => {
      onClose(event.wasClean ? 'normal' : 'abnormal')
    })
    return new Promise<void>((resolve, reject) => {
      socket.addEventListener('open', () => resolve(), { once: true })
      socket.addEventListener('error', () => reject(new Error('session upgrade failed')), { once: true })
    })
  }

  send(frame: ClientFrame) {
    if (this.#socket?.readyState !== WebSocket.OPEN) throw new Error('session is not open')
    this.#socket.send(JSON.stringify(frame))
  }

  close() {
    this.#socket?.close(1000, 'client disconnect')
    this.#socket = null
  }
}

const client = new SessionClient({
  transport: new BrowserWebSocketTransport(),
  capability: async () => {
    const response = await fetch('/api/lots/SKU-4471/capability', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
    })
    if (!response.ok) throw new Error('capability unavailable')
    return (await response.json()).capability as string
  },
})

await client.connect()

Recovery discipline

  1. 1

    Let the SDK acknowledge heartbeats

    SessionClient answers session.heartbeat frames. A missed deadline closes the connection; application activity is not a heartbeat substitute.

  2. 2

    Reconnect with fresh authority

    After an abnormal close, obtain a new capability, reconnect, and issue one explicit session.resync.

  3. 3

    Reconcile before deciding

    Read yourParticipation.commandOutcomes from the snapshot. Never automatically replay a bid or proxy command whose outcome is unknown.

  4. 4

    Respect congestion

    Surface overloaded and rate_limited with their optional retryAfterMs. Retrying an economic command remains an explicit user decision with a new request identifier.

Service credential delivery

During the private Sandbox pilot, headless.bid installs the credential directly into the tenant's approved deployment secret store. It is not emailed, returned to browser JavaScript, committed to a repository, printed in CI, or exposed by Platform OAuth. Rotate immediately after suspected disclosure.