Installation
Install the packages you need:npm install @keystoneos/react
1. Create a Session Token (Backend)
Session tokens are scoped, short-lived JWTs that your frontend uses to call the KeyStone API. Your backend creates them using M2M credentials.// your-backend/routes/keystone-session.ts
import { KeystoneClient } from '@keystoneos/sdk';
import { createSessionToken } from '@keystoneos/node';
const keystone = new KeystoneClient({
clientId: process.env.KEYSTONE_CLIENT_ID,
clientSecret: process.env.KEYSTONE_CLIENT_SECRET,
environment: 'production',
});
app.post('/api/keystone/session', async (req, res) => {
const session = await createSessionToken(keystone, {
scopes: ['settlements:read', 'settlements:write'],
metadata: { userId: req.user.id },
});
res.json({ token: session.sessionToken });
});
2. Set Up the Provider (Frontend)
Wrap your app (or the relevant section) withKeystoneProvider:
import { KeystoneProvider } from '@keystoneos/react';
function App() {
const [token, setToken] = useState<string | null>(null);
useEffect(() => {
fetch('/api/keystone/session', { method: 'POST' })
.then(r => r.json())
.then(d => setToken(d.token));
}, []);
if (!token) return <Loading />;
return (
<KeystoneProvider
sessionToken={token}
environment="production"
onTokenExpired={async () => {
const res = await fetch('/api/keystone/session', { method: 'POST' });
const data = await res.json();
return data.token;
}}
>
<YourApp />
</KeystoneProvider>
);
}
3. Use Hooks
Now you can use any hook inside the provider:import { useSettlements, getStateInfo } from '@keystoneos/react';
function SettlementDashboard() {
const { settlements, total, isLoading } = useSettlements();
if (isLoading) return <Spinner />;
return (
<table>
<thead>
<tr><th>Reference</th><th>State</th><th>Created</th></tr>
</thead>
<tbody>
{settlements.map(s => {
const state = getStateInfo(s.state);
return (
<tr key={s.id}>
<td>{s.external_reference}</td>
<td>{state.label}</td>
<td>{new Date(s.created_at).toLocaleDateString()}</td>
</tr>
);
})}
</tbody>
</table>
);
}
4. Submit an Instruction
Create a settlement by submitting your side of the trade:import { useSubmitInstruction } from '@keystoneos/react';
function TradeForm() {
const { submit, result, isSubmitting, error } = useSubmitInstruction();
const handleSubmit = async (formData) => {
const instruction = await submit({
role: 'seller',
party: {
external_reference: formData.accountId,
name: formData.accountName,
wallet_address: formData.walletAddress,
},
legs: [{
instrument_id: formData.instrumentId,
quantity: formData.quantity,
direction: 'deliver',
chain_id: formData.chainId,
}],
templateSlug: 'dvp-bilateral',
timeoutAt: new Date(Date.now() + 86400000).toISOString(),
});
if (instruction.status === 'matched') {
// Counterparty already submitted - settlement created
router.push(`/settlements/${instruction.settlementId}`);
}
};
if (result && result.status === 'pending_match') {
return (
<div>
<p>Waiting for counterparty. Share this trade reference:</p>
<code>{result.tradeReference}</code>
</div>
);
}
return <YourFormComponent onSubmit={handleSubmit} loading={isSubmitting} error={error} />;
}
5. Handle Deposits (Tier 2)
For custody wallet integration, provide action delegates:<KeystoneProvider
sessionToken={token}
actionDelegates={{
onDepositRequired: async (leg, depositInfo) => {
// depositInfo contains: calldata, escrowAddress, chainId, tokenAddress, amount
const result = await fetch('/api/deposit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
calldata: depositInfo.calldata,
escrowAddress: depositInfo.escrowAddress,
chainId: depositInfo.chainId,
}),
}).then(r => r.json());
return { txHash: result.txHash };
},
}}
>
// your-backend/routes/deposit.ts
app.post('/api/deposit', async (req, res) => {
const { calldata, escrowAddress, chainId } = req.body;
// Submit to Fireblocks, BitGo, or your signing infrastructure
const txHash = await fireblocksClient.submitTransaction({
to: escrowAddress,
data: calldata,
chainId,
});
res.json({ txHash });
});
6. Handle Webhooks (Backend)
Set up webhook processing with signature verification:import { webhookMiddleware } from '@keystoneos/node';
import express from 'express';
app.post(
'/webhooks/keystone',
express.raw({ type: 'application/json' }),
webhookMiddleware({ secret: process.env.WEBHOOK_SECRET }),
(req, res) => {
const { event, data } = req.keystoneEvent;
switch (event) {
case 'settlement.state.compliance_cleared':
console.log(`Settlement ${data.settlement_id}: ${data.to_state}`);
// Update your OMS, notify traders, etc.
break;
case 'settlement.state.finalized':
console.log(`Settlement finalized: ${data.settlement_id}`);
break;
}
res.sendStatus(200);
},
);
Next: Hooks Reference
Explore the full hooks API.