Node embed SDK
@spreadspace/embed is the server-side companion for embedding SpreadSpace.
Use it to mint short-lived embed sessions and iframe handles from your backend,
and to verify webhook signatures. It is the same typed client documented on the
TypeScript SDK page, highlighted here for the embed flow.
Server-side use only. Never ship this package to a browser: API keys must never
appear in client code. Mint a short-lived token server-side and pass only that
token, or the signed_url, to the browser.
Install
Construct the client
Other constructor options: baseUrl (override the base URL),
apiVersion (pins the SpreadSpace-Version header), timeout (milliseconds
per attempt), and maxRetries.
Mint an embed session
An embed session is a token scoped to one loan. Mint it server-side, then pass
session.embed_token to the browser. The token is locked to that single loan
and expires automatically (default one hour, maximum 24 hours).
Optional fields on the request body: scopes (a subset of the calling key’s
scopes; omit to inherit the read-only default: the in-widget upload and
review scopes documents:write / extractions:write are granted only when
named explicitly, and stay bound to the session’s loan), expires_in_seconds
(defaults to 3600, capped at 86400), and external_user_id (a stable id for
your end user, covered under per-user state below). Revoke a session early
when your UI flow ends before its natural expiry:
Mint an iframe handle
For the <iframe> integration used by @spreadspace/react,
mint a single-use handle instead. Your backend returns the signed_url, which
the SDK drops into the iframe source. The handle is exchanged for the token at
SpreadSpace’s origin, so the token never crosses into the parent page.
The handle is short-lived by design (default 60 seconds, maximum 300). For a longer-lived browser token, mint an embed session instead.
Per-user state
Both mint shapes accept an optional external_user_id: an opaque, stable
identifier for the end user you are embedding for, up to 256 characters. Use
a durable id from your own system rather than an email address, which can
change. Supply it and the embed keeps that user’s state on the server, keyed
to your organization and the id you send, so ids only need to be unique
within your own system. Saved boards and view settings, and the memo template
library, follow the user across sessions and devices. Omit the field and each
session starts from defaults.
The session response echoes external_user_id back, trimmed, and omits the
field when the mint carried none. On the iframe path the id is stored on the
handle and copied onto the session at exchange, so both routes yield the same
identity-carrying session. Send the same id for the same person on every
mint. An empty string is rejected rather than treated as omitted, so a bug
that sends one fails loudly.
display_name is optional, up to 120 characters. Reserved. Stored with the id,
not shown anywhere yet. It is echoed back, never written to a log and never
placed in the token. It requires external_user_id in the same request, and
sending one alone is rejected.
Every session response also carries persistence, which says which mode you
got: user when the mint included an external_user_id, session when it
did not. A session value means the embed renders and works normally but
saves nothing for the viewer. Assert on it in your integration tests, so a
dropped external_user_id fails your build instead of quietly discarding a
day of your analyst’s work.
Verify a webhook
Verify the spreadspace-signature header against the exact bytes you received,
never a re-serialized JSON object, or the HMAC will not match.
verifyAndParseWebhook throws WebhookSignatureError on any failure and
otherwise returns the parsed, typed event. The verifier is a tree-shakeable
@spreadspace/embed/webhooks import for receiver functions.
Scopes
The API key that mints embed sessions and iframe handles needs the
embed:write scope. Issue a key from the dashboard at Settings → Live
API (or Settings → Sandbox API for a test key) and grant it
embed:write. Minting is a service-account operation, so
use an API key, not a user session. The browser-facing token that results
never carries embed:write, so a leaked token cannot mint further
sessions or widen its own scope.
The security model
The token minting stays on your server; only a short-lived, loan-scoped
token (read-only unless the upload/review writes were explicitly minted), or
a single-use signed_url, ever reaches the browser. An
embed token is locked to the one loan it was minted for and cannot mint further
tokens. Revoking embed:write from the minting key immediately severs every
issued token and in-flight handle, and a per-session revoke is available for
targeted teardown.
Errors
Every API error maps to a typed error you can match with instanceof. Each
carries requestId (the server-generated X-Request-ID), type (the canonical
error-type string), and statusCode. Quote requestId in support tickets.