API integration guide
A test-mode key (ss_test_…) mints from Settings → Sandbox API with the
same presets as a live key, so it holds the same scopes, and it routes every
call to the sandbox: seeded loans, and uploads that return premade extractions
at no charge. This walkthrough runs on a live key because the Finalize click
happens in your workspace, on a live loan, and the receiver reads the snapshot
with a key that can see that loan. What a test key does exercise is delivery:
an upload through it (the Quick start run) fires
document.processed, extraction.ready, loan.classified and
job.completed at every endpoint on your workspace subscribed to them, each
with livemode: false.
When an analyst selects Finalize, SpreadSpace locks the spread, saves an
immutable snapshot, and sends a spread.finalized webhook. Your application
retrieves that snapshot by ID and stores its figures. If the analyst reopens
the spread, a spread.reopened event lets you mark the saved figures as under
revision. This guide covers both events, snapshot retrieval, and delivery testing.
What you need: an activated SpreadSpace workspace with at least one loan
that has documents, a key from Settings → Live API holding spreads:read,
webhooks:read and webhooks:write (the Backend preset holds all three),
and somewhere your receiver can be reached over https. A tunnel
(cloudflared, ngrok) in front of your laptop is fine.
1. Register your endpoint
Register an endpoint for spread.finalized and spread.reopened so your
application can track approvals and subsequent revisions.
The 201 returns signing_secret (whsec_…) exactly once. Store it
now. To change what the endpoint receives later, update it (PATCH /api/webhooks/{id} with subscribed_events) rather than registering again: a
second registration of the same URL is a second endpoint with its own secret.
2. Run the receiver
The SDK receiver verifies the signature over the raw bytes, drops a redelivery you have already processed, reads the snapshot the event names by id, and answers with the status the retry policy expects. You write the two handlers.
Build the receiver once per process, not per request. The default dedupe store lives on it. Hand it the raw bytes: a framework that parses JSON before your handler sees it has already changed the bytes the signature covers. The full option list is on Receive events with the SDK.
3. Finalize a spread
In the workspace (or inside your embed), open a saved spread on a loan whose board is scoped to a business filer (the figures come from the returns), then choose Finalize spread from the board menu. Confirm.
The board locks the moment the server answers: the menu now offers Reopen
spread, and replace, delete and a second finalize all refuse with
423 spread_locked until someone reopens it.
4. What arrives
One delivery reaches your endpoint, typically within seconds; allow up to a minute. This is the body (pretty-printed here; the signature covers the exact bytes as sent):
The body is a pointer, not the figures. Alongside it:
SpreadSpace-Event-Id equal to the body’s id,
SpreadSpace-Signature: t=…,v1=…, and
User-Agent: SpreadSpace-Webhooks/1.
Before your handler ran, the receiver read the snapshot the event names:
and handed it to you as attributes. It answers the snapshot itself, with no
{ loan_id, snapshot } wrapper on the by-id read:
(Money values are elided above; on the wire each is a string with exactly two decimals. Object key order on the wire is not the order shown here, so bind by name, never by position.)
Periods without a value remain present as null. For API-key requests,
finalized_by is null; use finalized_by_external_user_id to identify the
analyst through your own user records. This is the external_user_id supplied
when the embed session was created. See the
attribute catalog for supported names, units, and bases.
5. Handle a reopened spread
Choose Reopen spread from the same menu. The lock clears, the snapshots stay, and your endpoint receives:
snapshot_id is the final snapshot that was current. It is unchanged and
stays on record. The figures are under revision until the next
spread.finalized, which arrives with a higher version and a new
snapshot_id. Mark your copy stale, and let the next finalize replace it.
6. Test delivery and replay
Use GET /api/webhooks/{id}/deliveries to find a delivery, then verify replay
and signing-secret rotation:
- Replay the finalize delivery. It arrives as a new delivery with the
same event
id, signed with the endpoint’s current secret. Your receiver answers200 {"received":true,"duplicate":true}and your row count does not change. - Rotate the secret (
POST /api/webhooks/{id}/rotate), pass both secrets to the receiver for the 24-hour window, and finalize again: the new delivery verifies against the new secret only.
Store approved figures by snapshot_id and use the spread events to keep
their revision status current.
Up next
- Spreads: the lifecycle, the payload and the manifest, key by key.
- Webhooks: every event, the envelope, delivery and replay.
- Embed integration guide: run the workspace inside your own product so the Finalize click happens there; Embed has the packages and the security model.