@spreadspace/embed
v0.1.2
Published
Server-side Node.js SDK for SpreadSpace embeds — mint embed sessions and verify webhooks.
Maintainers
Readme
@spreadspace/embed
Server-side Node.js SDK for SpreadSpace embeds —
mints the short-lived embed sessions that authorize the browser widget, and
verifies SpreadSpace webhooks. For general API access from TypeScript, use
@spreadspace/sdk.
npm install @spreadspace/embed
# or
pnpm add @spreadspace/embedServer-side use only. Don't ship this package to a browser — API keys must
never appear in client code. For browser embedding, use the
<SpreadSpaceReview /> widget with a short-lived embed token minted via this
SDK.
Quickstart
1. Mint an embed token
import { SpreadSpaceClient } from '@spreadspace/embed';
const client = new SpreadSpaceClient({
apiKey: process.env.SPREADSPACE_API_KEY!, // 'ss_live_...' or 'ss_test_...'
});
const session = await client.embed.sessions.create({
loan_id: 'loan_abc',
});
// Pass session.embed_token to the browser. It is scoped to this single loan
// and expires automatically.2. Verify a webhook
import { verifyAndParseWebhook } from '@spreadspace/embed/webhooks';
app.post('/webhooks/spreadspace', async (req, res) => {
const event = verifyAndParseWebhook(
req.rawBody, // the exact bytes received — not re-stringified JSON
req.headers['spreadspace-signature'] as string,
process.env.SPREADSPACE_WEBHOOK_SECRET!,
);
switch (event.type) {
case 'extraction.ready':
// event.data is typed as ExtractionReadyPayload
await markExtractionReady(event.data.loan_id, event.data.extraction_id);
break;
case 'job.completed':
// event.data is typed as JobCompletedPayload
break;
}
res.status(200).end();
});3. Iterate over loans
for await (const loan of client.loans.list({ borrower_id: 'br_xyz' })) {
console.log(loan.id, loan.borrower_id);
}The list iterator handles cursor pagination automatically — no manual
next_cursor plumbing.
What you get
- Typed client for every endpoint, organized into Stripe-style
resources (
client.loans,client.borrowers,client.documents, etc.). - Automatic retries on
429and5xxresponses with exponential backoff and full jitter; honorsRetry-After. - Auto-generated
Idempotency-Keyon every non-GET request so retries are safe by default. Override or suppress per-request. - Auto-pinned
SpreadSpace-Versionheader — the SDK ships with the API surface it was built against. - Webhook signature verifier — byte-for-byte compatible with the
server-side signer, available as a tree-shakeable
@spreadspace/embed/webhooksimport for receiver Lambdas. - Async iterators for paginated list endpoints (
for await,.toArray(), or.pages()for batch processing). - Typed errors —
SpreadSpaceError/RateLimitError/PermissionError/ etc. — forinstanceof-based handling.
Configuration
new SpreadSpaceClient({
apiKey: 'ss_live_...',
baseUrl: 'https://api.spreadspace.app', // override for staging / local
apiVersion: '2026-05-03', // override the SDK-pinned version
timeout: 30_000, // ms per HTTP attempt
maxRetries: 3, // retry budget for 429 / 5xx
});Errors
import { SpreadSpaceClient, RateLimitError, PermissionError } from '@spreadspace/embed';
try {
await client.borrowers.retrieve('br_xyz');
} catch (err) {
if (err instanceof PermissionError && err.type === 'pii_claim_required') {
// err.details.borrower_id, err.details.claim_endpoint are set
} else if (err instanceof RateLimitError) {
// SDK already retried `maxRetries` times — the integrator may want to
// back off harder or surface to the caller.
}
}Every thrown SpreadSpaceError carries requestId (the server-generated
X-Request-ID), type (the canonical error-type string from the API
envelope), and statusCode. Quote requestId in support tickets.
License
MIT.
