Embed

Embed a SpreadSpace surface directly in your own application. Your users work with the spreading workspace or the document review panel without leaving your product, and without your page ever holding a SpreadSpace token.

Two packages cover the integration:

PackageRegistryRunsPurpose
@spreadspace/reactnpmBrowserRenders the embedded surface via <SpreadSpaceEmbed />.
@spreadspace/embednpmServerMints the short-lived session or handle that authorizes the embed.

How it fits together

  1. Your backend uses @spreadspace/embed (or a direct API call) with an API key that holds the embed:write scope to mint a single-use handle or a loan-scoped session.
  2. Your app renders <SpreadSpaceEmbed /> from @spreadspace/react and gives it a getHandle callback that fetches that handle from your backend.
  3. The SDK loads the returned signed_url into an iframe served from SpreadSpace’s origin. Inside the iframe, the handle is exchanged for a short-lived, loan-bound embed token that stays in the iframe and never reaches your page.

Install

$npm i @spreadspace/react

The security model

Your page never holds a SpreadSpace token. The token is minted server-side, exchanged inside a same-origin iframe, and kept there. A handle is single-use, expires in seconds, and is worthless off SpreadSpace’s origin. The browser-facing token is read-only by default and locked to a single loan. The only grantable writes are the explicit per-mint upload, review and spread scopes (documents:write, extractions:write, spreads:write), which stay bound to that same loan. A leaked token therefore cannot reach other loans or mint further tokens. The embed:write scope, held only by your server-side API key, is the control point: revoking it immediately severs every issued token and in-flight handle.

Persistent user state

Pass an optional external_user_id (your app’s opaque identifier for the viewing user, up to 256 characters) when minting a session or iframe handle. Sessions minted with the same id share server-kept state for that user: board arrangements, view preferences, and saved memo templates survive across that user’s sessions and devices. Omit it and each session starts from defaults.

The mint response says which mode you got. persistence is user when an external_user_id was supplied and session when it was not, and session means nothing the viewer arranges is ever saved.

The id also comes back to you: when that user finalizes a spread, the attribute snapshot carries it as finalized_by_external_user_id, so your backend can say who finalized from its own user table. SpreadSpace never has to hand a person’s name to your service key.

You can also pass an optional display_name (up to 120 characters) beside the id. Reserved. Stored with the id, not shown anywhere yet. It requires external_user_id in the same request.

How the numbers reach your backend

The analyst finishes the spread inside the embed and finalizes it there. Your backend receives a spread.finalized webhook naming the loan and the snapshot it just created, then reads the frozen figures by id with GET /api/loans/{loanId}/attributes/{snapshotId} (the same attribute snapshot the workspace produces) straight into your own fields. Nobody re-keys anything, and the numbers your system stores are the ones the analyst approved. If the analyst later reopens the spread to revise it, spread.reopened names that same snapshot so you can mark your copy stale until the next finalize arrives.

A session whose user is expected to finalize must be minted with spreads:write (or the wider extractions:write). The SDK’s webhook receiver reads the snapshot the event names for you on spread.finalized, so the handler you write is the half that stores it.

Quickstarts