Zero to verified chain
in under ten minutes.
By the end of this guide you will have created a chain, generated confirmation links for three actors, and verified the completed audit trail. No configuration beyond an API key.
The SwiftProof JavaScript SDK is published on npm. Install it in your project.
npm install swiftproof
@types/swiftproof package needed.
Email hello@tryswiftproof.com with your project name and use case. We will provision your API key and project within one business day.
Store your API key in an environment variable. Never commit it to version control.
SWIFTPROOF_API_KEY=sp_live_your_key_here
A chain is a sequence of steps that need to happen in order.
Each step has an actor responsible for confirming it. Define the
steps, call chains.create(), and you get back a
confirmation link for each actor and a tracking link for the end
customer.
import { SwiftProof } from 'swiftproof'; const sp = new SwiftProof({ apiKey: process.env.SWIFTPROOF_API_KEY, }); const chain = await sp.chains.create({ title: 'Order #4821 — Delivery to Lekki Phase 1', metadata: { orderId: '4821', customerPhone: '08012345678', }, actors: [ { event: 'packed', actor: 'Kitchen — Surulere' }, { event: 'collected', actor: 'Rider — Emeka' }, { event: 'delivered', actor: 'Rider — Emeka' }, ], }); console.log(chain);
The response contains a confirmation URL for each actor and a tracking URL for the customer.
{
"chain_id": "sp_ch_a96fec076ffd44f4",
"status": "open",
"tracking_url": "https://proof.tryswiftproof.com/track/abc123",
"handoffs": [
{
"event": "packed",
"actor": "Kitchen — Surulere",
"confirmation_url": "https://proof.tryswiftproof.com/confirm/tok_001",
"expires_at": "2026-06-26T08:14:00Z"
},
{
"event": "collected",
"actor": "Rider — Emeka",
"confirmation_url": "https://proof.tryswiftproof.com/confirm/tok_002",
"expires_at": "2026-06-26T08:14:00Z"
},
{
"event": "delivered",
"actor": "Rider — Emeka",
"confirmation_url": "https://proof.tryswiftproof.com/confirm/tok_003",
"expires_at": "2026-06-26T08:14:00Z"
}
]
}
| Field | Type | Description |
|---|---|---|
| chain_id | string | Unique identifier. Save this — you will use it to verify later. |
| status | string |
open until all actors confirm. Then
completed.
|
| tracking_url | string | Share with the end customer. Shows real-time progress. No app required. |
| handoffs[].confirmation_url | string | Send to the actor via WhatsApp or SMS. One-time use. |
| handoffs[].expires_at | string | ISO 8601 timestamp. Link expires after 48 hours by default. |
Distribute each confirmation URL to the relevant actor. The confirmation page works in any mobile browser. No app required. No login required.
// Send to kitchen via WhatsApp await sendWhatsApp( kitchenNumber, `Order #4821 is ready to pack.\nConfirm here: ${chain.handoffs[0].confirmation_url}` ); // Send to rider await sendWhatsApp( riderPhone, `Collect order #4821.\nConfirm collection and delivery here: ${chain.handoffs[1].confirmation_url}` ); // Send tracking link to customer await sendSMS(customerPhone, `Track your order: ${chain.tracking_url}`); // Save chain_id to your database await db.orders.update({ id: orderId, swiftproofChainId: chain.chain_id });
Call chains.verify() with the
chain_id at any point. The response tells you
whether the chain is verified, whether it has been tampered
with, and the full audit trail with GPS coordinates and
timestamps for every step.
const proof = await sp.chains.verify('sp_ch_a96fec076ffd44f4'); if (proof.tampered) { console.error('Chain has been tampered with'); return; } for (const step of proof.trail) { console.log(step.event, step.confirmed_at, step.location, step.valid); }
{
"verified": true,
"tampered": false,
"trail": [
{ "event": "packed", "actor": "Kitchen — Surulere", "confirmed_at": "2026-06-24T08:14:00Z", "location": "Ogunlana Drive, Surulere, Lagos", "valid": true },
{ "event": "collected", "actor": "Rider — Emeka", "confirmed_at": "2026-06-24T08:47:00Z", "location": "Ogunlana Drive, Surulere, Lagos", "valid": true },
{ "event": "delivered", "actor": "Rider — Emeka", "confirmed_at": "2026-06-24T09:22:00Z", "location": "Admiralty Way, Lekki Phase 1, Lagos", "valid": true }
]
}
Pass an Idempotency-Key header on every chain
creation call to ensure retries never create duplicate chains.
const chain = await sp.chains.create({ title: 'Order #4821', actors: [...], }, { idempotencyKey: `order-4821-chain`, }); // Safe to retry on network failure. // If the key already exists, the original response is returned.
Keep building.
You have the foundation. Here is where to go next.