transcrypts
v1.1.0
Published
Transcrypts SDK — open the verification widget from your frontend
Maintainers
Readme
Transcrypts SDK
Browser SDK for Transcrypts employment & income verification. Opens the verification widget in an iframe overlay; the user authenticates with their preferred data source (Verify with Transcrypts, IRS, and more) and your server receives the verified result via signed webhooks.
Installation
npm install transcrypts
# or
yarn add transcrypts
# or
pnpm add transcryptsTwo-environment model
Transcrypts runs two fully isolated environments:
| Environment | Host | When |
|---|---|---|
| Sandbox | sandbox.transcrypts.com | Build against magic credentials. No real PII, no real verifications. |
| Production | app.transcrypts.com | Real users, real data, real Browserbase. |
Each environment has its own keys (sandbox pk_live_* / sk_live_* don't work in production and vice versa) and its own webhook endpoints. Promotion is a one-line change.
Quick start
1. Create a verification session (server-side)
Your server calls POST /v1/verification_sessions with your secret key. You receive a short-lived client_secret to forward to your frontend.
// On your server
const session = await fetch(
`${process.env.TRANSCRYPTS_BASE_URL}/api/sdk/v1/verification_sessions`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TRANSCRYPTS_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
applicant_hint: {
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
},
metadata: { your_internal_id: 'LN_12345' },
}),
}
).then((r) => r.json());
// Forward session.client_secret to your frontend.2. Open the widget (frontend)
import { Transcrypts } from 'transcrypts';
const transcrypts = new Transcrypts({
publishableKey: 'pk_live_...',
mode: 'sandbox', // Drop or set to 'production' when you go live.
});
const result = await transcrypts.verify({
clientSecret, // from your server
});
// result is UI signal only — no PII.
// { sessionId, status: 'completed' | 'pending' | 'failed' | 'canceled' }
console.log(result.sessionId, result.status);3. Receive the result (server-side webhook)
The canonical verified data is delivered to your registered webhook endpoint. The browser never sees PII.
import crypto from 'crypto';
app.post('/webhooks/transcrypts', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-transcrypts-signature'];
const secret = process.env.TRANSCRYPTS_WEBHOOK_SECRET;
const body = req.body.toString('utf8');
// Parse "t=<timestamp>,v1=<hex>", verify HMAC and timestamp.
const parts = Object.fromEntries(signature.split(',').map((p) => p.split('=')));
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${body}`)
.digest('hex');
if (
Math.abs(Date.now() / 1000 - parseInt(parts.t, 10)) > 300 ||
!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
) {
return res.status(401).end();
}
const event = JSON.parse(body);
if (event.type === 'verification_session.verified') {
// event.data.result_data.employers / earnings is the normalized result.
// event.data.metadata round-trips what you passed at session creation.
}
res.json({ received: true });
});API reference
new Transcrypts(options)
| Option | Type | Required | Description |
|---|---|---|---|
| publishableKey | string | Yes | Your publishable key (pk_live_*). |
| mode | 'sandbox' \| 'production' | No | Which environment to target. Defaults to 'production'. Use 'sandbox' while building against magic credentials. |
| apiBaseUrl | string | No | Explicit base URL. Overrides mode when set. Use for local development (http://localhost:3000) or custom domains. Most integrators don't need this. |
| containerStyle | Partial<CSSStyleDeclaration> | No | Custom styles for the iframe overlay container. |
transcrypts.verify(options)
Opens the widget and resolves when the user finishes or closes it.
| Option | Type | Required | Description |
|---|---|---|---|
| clientSecret | string | Yes | Single-use cs_* from your server's POST /v1/verification_sessions response. |
Returns Promise<VerificationResult>:
interface VerificationResult {
sessionId: string | null;
status: 'completed' | 'pending' | 'failed' | 'canceled';
}This is a UI signal only — never includes PII. To read the verified data, handle the webhook on your server or call GET /v1/verification_sessions/:id with your secret key.
transcrypts.on(eventType, callback)
Subscribe to widget lifecycle events. Returns an unsubscribe function.
const off = transcrypts.on('verification_session_completed', (event) => {
// event.payload = { sessionId, status }
});
// Later: off();| Event | When |
|---|---|
| ready | Widget iframe loaded. |
| verification_session_completed | User finished the flow. Payload: { sessionId, status }. |
| close | User dismissed the widget. |
Architecture
The SDK is ~5KB and framework-agnostic. The widget itself runs in an iframe served from the same origin as the API:
┌─────────────────────────────────────────────────┐
│ Your app (React / Vue / Angular / vanilla) │
│ │
│ transcrypts.verify({ clientSecret }) │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ <iframe src= │ │
│ │ https://{mode}.transcrypts.com/widget │ │
│ │ #cs=cs_*&pk=pk_live_*> │ │
│ │ │ │
│ │ User authenticates with their data │ │
│ │ source; widget never posts PII back to │ │
│ │ your page. │ │
│ └─────────────────────────────────────────┘ │
│ │
│ postMessage({ sessionId, status }) │
└─────────────────────────────────────────────────┘
│
▼
signed webhook → your server (canonical data)Why iframe?
- Bundle size: SDK doesn't ship React.
- Isolation: widget runs in its own context; you can't intercept PII from your page.
- Atomic updates: improvements to the widget UI ship without an SDK release.
Full documentation
API endpoints, magic-credential reference, webhook event types, signature verification, retry policy, white-label branding, dashboard usage:
Support
Questions or early-access requests: [email protected].
License
MIT
