Skip to main contentSkip to FAQSkip to contact
For OpssHands-on· 10 min

Authentication#

All TetraFi API requests require authentication via Bearer token. This page covers API key management, environments, rate limits, error handling, and pagination.

30-second proof of life. Set your API key and hit the status endpoint. If you get 200 OK you're authenticated and ready to make real requests:

Bash
1export TETRAFI_API_KEY=tfk_test_...
2curl -H "Authorization: Bearer $TETRAFI_API_KEY" https://sandbox.tetrafi.io/api/v1/health
3# { "ok": true, "environment": "sandbox", "version": "v1" }
3 linesbash

API Keys#

TetraFi uses two types of API keys:

TypePrefixEnvironmentReal Funds
Sandboxtfk_test_Testing & developmentNo
Productiontfk_live_Live tradingYes

Both key types work identically - sandbox never touches real funds and compliance checks are mocked. Generate keys from the TetraFi dashboard.

Key Format

tfk_{env}_{key_id}_{random_hex} - e.g., tfk_live_a1b2c3_8f4e...

Never expose API keys in client-side code. Store them in environment variables and use server-side requests only. Key rotation: generate a new key, update your config, the old key expires in 24 hours.

Endpoint Auth Map#

Not every endpoint needs authentication. The table below shows the auth tier per endpoint:

EndpointAuth TierHeader Required
GET /api/v1/healthPublicNone
POST /api/v1/quotesPublicNone
GET /api/v1/quotes/{rfqId}PublicNone
POST /api/v1/ordersOptional AuthAPI key recommended (Direct path) or none (Compliant path)
GET /api/v1/orders/{id}PublicNone
POST /api/v1/auth/challengePublicNone
POST /api/v1/auth/verifyPublicNone
GET /api/v1/auth/meRequiredJWT (Bearer)

X-TetraFi-Attestation is only required on compliance-sensitive operations (e.g., POST /orders when using the Compliant path). It is optional on public endpoints like POST /quotes.

Making Authenticated Requests#

Include your API key in the Authorization header. Optionally include your compliance attestation:

TypeScript
1const response = await fetch("https://api.tetrafi.io/api/v1/quotes", {
2 method: "POST",
3 headers: {
4 "Authorization": "Bearer tfk_live_...",
5 "Content-Type": "application/json",
6 // Optional: compliance attestation hash
7 "X-TetraFi-Attestation": "0x...",
8 },
9 body: JSON.stringify({
10 pair: "USDC/USDT",
11 side: "buy",
12 amount: "1000000",
13 }),
14});
15
16const data = await response.json();
16 linestypescript

Environments#

SandboxProduction
Base URLhttps://sandbox.tetrafi.io/api/v1https://api.tetrafi.io/api/v1
WebSocketwss://sandbox.tetrafi.io/api/v1/wswss://api.tetrafi.io/api/v1/ws
API Key Prefixtfk_test_tfk_live_
Real FundsNoYes
Rate LimitsSameSame
ComplianceMockedEnforced
SettlementTestnet (Sepolia)Mainnet
Bash
1# .env.local - sandbox
2TETRAFI_API_KEY=tfk_test_...
3TETRAFI_BASE_URL=https://sandbox.tetrafi.io/api/v1
4
5# .env.production
6TETRAFI_API_KEY=tfk_live_...
7TETRAFI_BASE_URL=https://api.tetrafi.io/api/v1
7 linesbash

Rate Limits#

Rate limits are enforced per API key. Default limits:

MetricLimit
Requests/min120
Burst/sec20
WebSocket connections5

Higher limits are available on request for high-volume integrations.

Rate Limit Headers#

Every response includes current rate limit information:

X-RateLimit-Limitnumber
Maximum requests allowed per window
X-RateLimit-Remainingnumber
Remaining requests in current window
X-RateLimit-Resettimestamp
UTC epoch when the window resets
Retry-Afternumber
Seconds to wait (on 429 responses only)

Handling Rate Limits#

TypeScript
1async function requestWithBackoff<T>(
2 fn: () => Promise<T>,
3 maxRetries = 5
4): Promise<T> {
5 for (let attempt = 0; attempt <= maxRetries; attempt++) {
6 try {
7 return await fn();
8 } catch (e) {
9 if (e instanceof RateLimitError && attempt < maxRetries) {
10 const wait = e.retryAfter ?? Math.min(30, 2 ** attempt);
11 console.log(`Rate limited - waiting {wait}s (attempt {attempt + 1})`);
12 await sleep(wait * 1000);
13 } else {
14 throw e;
15 }
16 }
17 }
18 throw new Error("Max retries exceeded");
19}
19 linestypescript

Error Handling#

All errors follow a consistent machine-readable format:

JSON
1{
2 "error": {
3 "type": "invalid_request",
4 "code": "pair_not_supported",
5 "message": "The trading pair DOGE/SHIB is not supported.",
6 "param": "pair",
7 "doc_url": "https://tetrafi.io/docs/errors#pair_not_supported"
8 }
9}
9 linesjson

HTTP Status Codes#

StatusTypeDescription
400invalid_requestMalformed request, missing required param
401authentication_errorInvalid or expired API key
403compliance_errorParticipant not compliant to trade
404not_foundResource (RFQ, quote, settlement) not found
409conflictDuplicate request (idempotency violation)
422validation_errorParam values are valid types but fail business rules
429rate_limit_errorToo many requests - use Retry-After header
500api_errorInternal server error - retry with backoff
503maintenanceScheduled maintenance - check status page

Common Error Codes#

CodeMeaning
pair_not_supportedThe pair isn't available on the requested corridor
amount_too_smallBelow minimum trade size ($10,000 USD equivalent)
amount_too_largeExceeds maximum trade size for your tier
attestation_expiredCompliance attestation needs renewal
corridor_unavailableNo solvers covering the requested corridor
rfq_expiredQuote window has passed - create a new RFQ

Idempotency#

For POST requests that create resources, include an Idempotency-Key header to safely retry failed requests:

TypeScript
1const response = await fetch("https://api.tetrafi.io/api/v1/quotes", {
2 method: "POST",
3 headers: {
4 "Authorization": "Bearer tfk_live_...",
5 "Content-Type": "application/json",
6 // Unique per request - same key = same response (for 24h)
7 "Idempotency-Key": crypto.randomUUID(),
8 },
9 body: JSON.stringify({ pair: "USDC/USDT", side: "buy", amount: "1000000" }),
10});
10 linestypescript

Pagination#

Long lists (quotes, audit entries, settlements) use cursor-based pagination:

TypeScript
1// First page
2const response = await fetch("https://api.tetrafi.io/api/v1/settlements?limit=25", {
3 headers: { "Authorization": "Bearer tfk_live_..." },
4});
5const { data, has_more, next_cursor } = await response.json();
6
7// Next page
8if (has_more) {
9 const next = await fetch(
10 `https://api.tetrafi.io/api/v1/settlements?limit=25&after=${next_cursor}`,
11 { headers: { "Authorization": "Bearer tfk_live_..." } }
12 );
13}
13 linestypescript
ParameterTypeDescription
limitnumberNumber of results per page. Max 100, default 25.
afterstringCursor from previous response's next_cursor. Fetches next page.
beforestringFetch the previous page (reverse pagination).

Response shape:

JSON
1{
2 "data": [...],
3 "has_more": true,
4 "next_cursor": "cursor_abc123",
5 "total_count": 1247
6}
6 linesjson

Common Auth Issues#

Quick Setup#

Authentication Setup

Language
TypeScript
1import { TetraFi } from '@tetrafi/sdk';
2
3const tetrafi = new TetraFi({
4 apiKey: process.env.TETRAFI_API_KEY!,
5 environment: 'sandbox',
6});
7
8// Verify connection
9const status = await tetrafi.status();
10console.log('Connected:', status.ok);
10 linestypescript

See Also#

I'm a TakerStep 5 of 7

Related topics