Skip to main contentSkip to FAQSkip to contact
Planned5 min read

TypeScript SDK#

Planned Specification. The @tetrafi/sdk package is under development and will be published to npm upon release. The API surface below is the target specification - use the REST API and WebSocket directly until release. Feedback on the SDK shape is welcome.

The @tetrafi/sdk package will provide a typed, ergonomic interface for the TetraFi API. Stripe-inspired design: a single client instance with namespaced methods.

Installation#

Install @tetrafi/sdk
Press play to start demo...

Initialization#

TypeScript
1import { TetraFi } from "@tetrafi/sdk";
2
3const tetrafi = new TetraFi({
4 apiKey: process.env.TETRAFI_API_KEY!,
5 environment: "sandbox", // "sandbox" | "production"
6 timeout: 30000, // request timeout (ms), default 30000
7 retries: 3, // auto-retry on 5xx, default 3
8});
8 linestypescript
index.tstypescript
TypeScript
1// Stubbed TetraFi client - drop in the real @tetrafi/sdk once installed.
2class TetraFi {
3constructor(cfg) { this.cfg = cfg; console.log("TetraFi initialised:", cfg.environment); }
4}
5
6const tetrafi = new TetraFi({
7apiKey: "tfk_test_example",
8environment: "sandbox",
9});
10
11console.log("Ready to submit RFQs.");
11 linestypescript
apiKeystringrequired
Your API key (tfk_test_* for sandbox, tfk_live_* for production)
environmentstring
API environment. Defaults to 'production'.Default: production
timeoutnumber
Request timeout in milliseconds.Default: 30000
retriesnumber
Auto-retry count on 5xx errors.Default: 3
baseUrlstring
Override the API base URL. Useful for proxies or local development.
loggerfunction
Custom logging function. Defaults to console.log in development.

Core Methods#

Method Index

MethodReturnsSinceStatus
rfq.create(params)RFQv2.0
Stable
rfq.getQuotes(rfqId)Quote[]v2.0
Stable
rfq.acceptBest(rfqId)Settlementv2.0
quotes.accept(quoteId)Settlementv2.0
settlements.get(id)Settlementv2.0
compliance.check(address)ComplianceStatusv2.0
audit.getTrail(id)AuditEntry[]v2.1
events.on(type, cb)() => voidv2.1
Beta

rfq.create(params) Stable#

Submit a new Request for Quote to competing solvers.

ParameterTypeDescription
pairrequiredstringTrading pair in QUOTE/BASE format, e.g. "USDC/USDT"
siderequired"buy" | "sell"Trade direction from the taker's perspective
amountrequiredstringTrade size as a decimal string in source token units
corridorstringCross-chain path, e.g. "ethereum-optimism". Defaults to same-chain.
maxSlippagenumberMaximum acceptable slippage (0.001 = 0.1%). Defaults to 0.005.
TypeScript
1const rfq = await tetrafi.rfq.create({
2 pair: "USDC/USDT",
3 side: "buy",
4 amount: "1000000.00",
5 corridor: "ethereum-optimism",
6 maxSlippage: 0.001,
7});
7 linestypescript
ReturnsRFQ

rfq.getQuotes(rfqId) Stable#

Get competing quotes for an active RFQ.

TypeScript
1const quotes = await tetrafi.rfq.getQuotes("rfq_abc123");
1 linestypescript
ReturnsQuote[]- sorted best-price first

rfq.acceptBest(rfqId)#

Automatically select and accept the best available quote.

TypeScript
1const settlement = await tetrafi.rfq.acceptBest("rfq_abc123");
1 linestypescript
ReturnsSettlement

quotes.accept(quoteId)#

Accept a specific quote and trigger escrow creation.

TypeScript
1const settlement = await tetrafi.quotes.accept("qt_xyz789");
1 linestypescript
ReturnsSettlement

settlements.get(id)#

Get the current status of a settlement.

TypeScript
1const settlement = await tetrafi.settlements.get("stl_abc123");
1 linestypescript
ReturnsSettlement

compliance.check(address)#

Check whether an address has a valid compliance attestation.

TypeScript
1const status = await tetrafi.compliance.check("...");
1 linestypescript

audit.getTrail(settlementId)#

Fetch the WORM audit trail for a completed settlement.

TypeScript
1const entries = await tetrafi.audit.getTrail("stl_abc123");
1 linestypescript
ReturnsAuditEntry[]

events.on(type, callback) Beta#

Subscribe to real-time events via WebSocket.

TypeScript
1const unsubscribe = tetrafi.events.on("settlement.complete", (event) => {
2 console.log("Settled:", event.settlement.id);
3});
4
5// Later: stop receiving events
6unsubscribe();
6 linestypescript
ParameterTypeDescription
typerequiredEventTypeEvent type to subscribe to. Use wildcards: "settlement.*" matches all settlement events.
callbackrequired(event: Event) => voidFunction called when a matching event arrives.

Type Definitions#

TypeScript
1interface RFQ {
2 id: string;
3 pair: string;
4 side: "buy" | "sell";
5 amount: string;
6 corridor: string;
7 status: "pending" | "quoting" | "accepted" | "expired";
8 createdAt: string; // ISO 8601
9 expiresAt: string; // ISO 8601
10}
11
12interface Quote {
13 id: string;
14 rfqId: string;
15 solverId: string;
16 price: string; // Decimal string, e.g. "0.9995"
17 ttl: number; // Seconds until expiry
18 confidence: number; // 0-1 fill confidence score
19 createdAt: string;
20}
20 linestypescript
TypeScript
1interface Settlement {
2 id: string;
3 rfqId: string;
4 quoteId: string;
5 status: "pending" | "escrow_locked" | "filling" | "complete" | "failed" | "refunded";
6 originTxHash?: string;
7 destinationTxHash?: string;
8 amount: string;
9 createdAt: string;
10 settledAt?: string;
11}
12
13interface ComplianceStatus {
14 address: string;
15 isCompliant: boolean;
16 attestationHash: string;
17 expiresAt: string;
18}
18 linestypescript
TypeScript
1interface AuditEntry {
2 tradeId: string;
3 type: "rfq_created" | "quote_accepted" | "escrow_locked" | "settlement_complete";
4 timestamp: string;
5 data: Record<string, unknown>;
6 hash: string; // WORM integrity hash
7 prevHash: string; // Chain link to previous entry
8}
9
10type EventType =
11 | "rfq.created"
12 | "quote.received"
13 | "quote.accepted"
14 | "settlement.pending"
15 | "settlement.complete"
16 | "settlement.failed"
17 | "audit.recorded"
18 | "rfq.*"
19 | "settlement.*";
19 linestypescript

Full Working Example#

TypeScript
1import { TetraFi } from "@tetrafi/sdk";
2
3const tetrafi = new TetraFi({
4 apiKey: process.env.TETRAFI_API_KEY!,
5 environment: "sandbox",
6});
7
8async function executeSwap() {
9 // 1. Create RFQ
10 const rfq = await tetrafi.rfq.create({
11 pair: "USDC/USDT",
12 side: "buy",
13 amount: "1000000.00",
14 });
15 console.log("RFQ created:", rfq.id);
16
17 // 2. Wait for quotes (poll)
18 await new Promise((r) => setTimeout(r, 3000));
19 const quotes = await tetrafi.rfq.getQuotes(rfq.id);
20 console.log("Received quotes:", quotes.length);
21
22 if (quotes.length === 0) {
23 throw new Error("No quotes received");
24 }
25
26 // 3. Accept best price
27 const settlement = await tetrafi.rfq.acceptBest(rfq.id);
28 console.log("Settlement initiated:", settlement.id);
29
30 // 4. Monitor to completion
31 let status = settlement;
32 while (!["complete", "failed", "refunded"].includes(status.status)) {
33 await new Promise((r) => setTimeout(r, 5000));
34 status = await tetrafi.settlements.get(settlement.id);
35 console.log("Status:", status.status);
36 }
37
38 if (status.status === "complete") {
39 console.log("Success!");
40 console.log("Origin TX:", status.originTxHash);
41 console.log("Destination TX:", status.destinationTxHash);
42 } else {
43 throw new Error(`Settlement failed: {status.status}`);
44 }
45}
46
47executeSwap().catch(console.error);
47 linestypescript

Error Handling#

TypeScript
1import { TetraFiError, RateLimitError, AuthError, ComplianceError } from "@tetrafi/sdk";
2
3try {
4 const rfq = await tetrafi.rfq.create({
5 pair: "USDC/USDT",
6 side: "buy",
7 amount: "1000000",
8 });
9} catch (e) {
10 if (e instanceof RateLimitError) {
11 // Exponential backoff
12 await sleep(e.retryAfter * 1000);
13 // retry...
14 } else if (e instanceof AuthError) {
15 console.error("Invalid API key - check TETRAFI_API_KEY");
16 } else if (e instanceof ComplianceError) {
17 console.error("Compliance check failed - ensure attestation is current");
18 } else if (e instanceof TetraFiError) {
19 console.error("API error:", e.code, e.message);
20 } else {
21 throw e; // Unknown error - rethrow
22 }
23}
23 linestypescript

WebSocket Event Stream#

For real-time updates, the SDK exposes a WebSocket event stream via tetrafi.events. The connection authenticates with your API key and supports wildcard channel subscriptions. Auto-reconnect is on by default.

TypeScript
1import { TetraFi } from "@tetrafi/sdk";
2
3const tetrafi = new TetraFi({
4 apiKey: process.env.TETRAFI_API_KEY!,
5 environment: "sandbox",
6});
7
8// Connect (lazy - happens on first .on() call)
9const unsubQuotes = tetrafi.events.on("quote.received", (event) => {
10 console.log(`[Quote] {event.quote.solverId}: {event.quote.price}`);
11});
12
13const unsubSettlement = tetrafi.events.on("settlement.*", (event) => {
14 switch (event.type) {
15 case "settlement.pending":
16 console.log("Escrow locked:", event.settlement.id);
17 break;
18 case "settlement.complete":
19 console.log("Complete!", event.settlement.originTxHash);
20 break;
21 case "settlement.failed":
22 console.error("Failed:", event.settlement.error);
23 break;
24 }
25});
26
27// Connection lifecycle hooks
28tetrafi.events.on("connection.opened", () => console.log("WS connected"));
29tetrafi.events.on("connection.closed", () => console.log("WS disconnected - auto-reconnecting"));
30tetrafi.events.on("connection.error", (e) => console.error("WS error:", e));
31
32// Cleanup on shutdown
33process.on("SIGTERM", () => {
34 unsubQuotes();
35 unsubSettlement();
36 tetrafi.events.disconnect();
37});
37 linestypescript

Auto-reconnect: the SDK reconnects automatically on connection loss with exponential backoff (1s → 2s → 4s → 8s, capped at 30s). Active subscriptions are restored after each reconnect - your .on() callbacks keep firing without re-subscribing.

Available Event Channels#

ChannelWhen it fires
rfq.createdYour RFQ has been broadcast to solvers
quote.receivedA solver returned a quote
quote.acceptedYou accepted a quote
settlement.pendingSettlement initiated, escrow locking
settlement.completeBoth legs settled atomically
settlement.failedSettlement reverted, refund issued
audit.recordedNew WORM audit entry
rfq.*Wildcard - all RFQ events
settlement.*Wildcard - all settlement events

Try It Live#

Event Stream

Subscribe to real-time quote and settlement events.

Versioning#

The @tetrafi/sdk package follows semver. Pin your dependency conservatively until you've confirmed compatibility in staging.

BumpMeaningAction required
1.0.x → 1.0.yPatch - bug fixes, no API changesAuto-update is safe
1.0.x → 1.1.xMinor - additive features, new methodsRead changelog, no migration
1.x.x → 2.0.0Major - breaking changesRead migration guide

Deprecation policy. Methods scheduled for removal are marked with a @deprecated JSDoc tag for at least one minor version before removal. Your IDE will surface a strikethrough on the call site, and the SDK logs a one-time warning at runtime:

text
1[TetraFi] Warning: tetrafi.rfq.create({ slippage }) is deprecated. Use { maxSlippage } instead. This will be removed in 2.0.0.
1 linestext

The full changelog lives at github.com/tetrafi/sdk-typescript/releases. Subscribe to the GitHub releases feed to be notified of new versions.

v1.4.2Current2026-04-01(1 months ago)
AddedHMAC pseudonymization for Travel Rule data fields
ChangedWebSocket reconnection uses exponential backoff (1s → 30s cap)
FixedRace condition in concurrent RFQ submissions when using shared client instance
v1.4.0Stable2026-03-15(2 months ago)
AddedDutch auction order type support via orderType: 'dutch' option
AddedComplianceRegistry attestation querying via compliance.check()
BreakingRenamed connect() to init() for consistency with Python SDK

Pinning tip. For production deployments, pin to an exact version ("@tetrafi/sdk": "1.4.2") rather than a caret range ("^1.4.2"). This prevents an automatic minor update from changing behavior between deploys. Upgrade explicitly after reading the changelog.

Live Playground#

Try the SDK directly in your browser:

How this works. Edit the code below and watch the output update live. The playground runs in a sandboxed iframe using a mock @tetrafi/sdk that returns realistic data with simulated latency - no real API calls are made. Try changing the amount, pair, or side to see how responses change.

Submit an RFQ

Create an RFQ, receive quotes, and accept the best price.