Realtime API

Server-Sent Events for agent execution updates, WebSocket for price ticks and OHLCV bars, and the trading-key (permit) flow that authorises agentic actions.

Two channels, two purposes

ChannelTransportPurpose
Agent Execution StreamSSEPer-wallet stream of agent lifecycle events: arming, executing, errors, completion.
Price Engine StreamWebSocketPer-asset stream of unified price ticks and OHLCV bar updates.

Agent Execution Stream (SSE)

GET /api/agents/stream

Per-wallet SSE stream. Auth is by walletAddress (query param) or by JWT via the requireWalletAddress middleware. The server emits an SSE comment heartbeat (: heartbeat\n\n) every 25 seconds; standard browser EventSource clients handle the heartbeat automatically.

Event types

EventPayload
executed{ agentId, executionId, rail, txSignature, filledAmount, filledPriceUsd, feeUsd }
errored{ agentId, executionId, reason }
next_run_changed{ agentId, nextRunAt }
trigger_armed{ agentId, condition }
completed{ agentId, terminalReason }
const es = new EventSource(
  `/api/agents/stream?walletAddress=${wallet}`
);
es.addEventListener('executed', (e) => {
  const d = JSON.parse(e.data);
  console.log('Filled', d.rail, d.filledAmount, '@', d.filledPriceUsd);
});
es.addEventListener('errored', (e) => {
  console.warn(JSON.parse(e.data));
});

Price Engine Stream (WebSocket)

GET wss://quotes.truefinance.ai/v1/stream

Single multiplexed socket for all price subscriptions on the connection. Auth via Bearer JWT or API key (the Authenticator middleware accepts either).

Connection lifecycle

01
Connect
Open WS with Authorization: Bearer <token>. Server replies with a hello frame carrying tier and limits.
02
Subscribe
Send { type: "subscribe", assets: ["SOL", "ETH", "BTC"] }.
03
Receive
Server emits { type: "price", data: UnifiedPrice } on every tick and { type: "bar_update", data: BarBroadcastEvent } on bar close.
04
Heartbeat
Server sends { type: "ping", ts } every 30s. Client must reply { type: "pong" } within 90s or the server closes.
05
Unsubscribe
{ type: "unsubscribe", assets: [...] } removes assets from the subscription.

Inbound frames

{ "type": "subscribe",   "assets": ["SOL", "BTC"] }
{ "type": "unsubscribe", "assets": ["BTC"] }
{ "type": "pong" }

Outbound frames

{ "type": "hello", "tier": "pro", "limits": { "maxAssets": 100, "maxConnections": 5 } }
{ "type": "price", "data": { "symbol": "SOL", "priceUsd": 178.41, "ts": 1746144000000, "source": "pyth", "stale": false } }
{ "type": "bar_update", "data": { "symbol": "SOL", "interval": "1m", "open": 178.20, "high": 178.51, "low": 178.05, "close": 178.41, "volume": 12340.5, "ts": 1746144000000 } }
{ "type": "ping", "ts": 1746144030000 }
{ "type": "error", "code": "symbol_limit", "message": "Tier cap reached" }

Caps and tiers

LimitFreeProQuant
Max concurrent connections1525
Max symbols per connection101001,000
Global cap15,000 connections
Per-IP cap50 connections

Errors the server may emit before close: bad_json, symbol_limit, unknown_type.

Trading-key (Permit) flow

The “trading key” is a permit — an Ed25519 signature over a canonical JSON message that scopes an ephemeral session wallet to specific agentic actions. There is no long-lived API key with trade authority. Every authorised execution traces back to a permit the user signed.

01
Provision session wallet
POST /v1/agents/session-wallet. Server generates a Solana keypair, KMS-encrypts the secret, returns the sessionPubkey.
02
Sign permit
User's primary wallet signs a permit message: { agentId, sessionPubkey, assetWhitelist, maxAmountPerExecutionUsd, expiresAt }.
03
Activate
POST /api/agents/{id}/activate with { permit, signature }. verifyPermit() checks the Ed25519 signature against the user's primary wallet pubkey.
04
Fund + fee
Server collects AGENT_ACTIVATION_FEE_LAMPORTS (0.02 SOL) from the session wallet to the Umbrella wallet. Strategy transitions to active.
05
Execute
On every fire, the executor runs a 2-stage permit check (Stage 1: expiry + assetWhitelist; Stage 2: USD amount vs maxAmountPerExecutionUsd post price-resolution) before signing.
06
Revoke
POST /api/agents/{id}/pause or /stop. Pause is reversible; stop is terminal and blocks any further executions on the permit.

Permit JSON

{
  "agentId": "uuid",
  "sessionPubkey": "Sess1...PubKeyBase58",
  "assetWhitelist": ["SOL", "USDC", "JTO"],
  "maxAmountPerExecutionUsd": 250,
  "expiresAt": "2026-08-01T00:00:00Z",
  "issuedAt": "2026-05-02T12:00:00Z",
  "nonce": "a1b2c3..."
}

The permit is signed with the user’s primary wallet. The server stores the bounds (without the raw signature) on the agent row so the executor can re-validate every fire.

A permit is the only path to autonomous execution

No environment variable, dashboard toggle, or admin override grants trade authority. If a permit is not on the agent, the executor will not sign. Permits cannot be edited after issuance — to change scope, the user must stop the agent and sign a new permit.

Caps that bound everything

  • 20 active agents per user.
  • 500 executions / 24h per user.
  • Per-trade USD cap from the permit (maxAmountPerExecutionUsd), enforced at signing time.
  • Daily SOL auto-fund cap per user (MAX_AUTOFUND_LAMPORTS_PER_USER_PER_DAY) for session-wallet top-ups.
For Developers

SSE and EventSource gotchas

EventSource reconnects on its own. Treat the SSE stream as best-effort delivery: every event carries an executionId so dedup is straightforward, and the REST endpoints (GET /api/agents/{id}, GET /api/agents/{id}/executions) are the source of truth for state reconciliation.

WS reconnect

If the server closes after a missed pong, reconnect with the existing token and re-subscribe to the asset list. Subscriptions are not persisted across connections.

Stable identifiers

executionId is unique per execution attempt. txSignature is unique per landed Solana transaction. Use txSignature for on-chain reconciliation; use executionId for cross-replica dedup.

Safety, limits, failure modes

  • Token expiry mid-stream. A JWT that expires while the WS is connected is not refreshed automatically; the next heartbeat will close the connection. Reconnect with a fresh token.
  • Stale-flagged ticks. Ticks may carry stale: true when the upstream oracle is degraded; do not trade on stale ticks. See Price Engine.
  • Permit expiry. An expired permit produces a PERMIT_EXPIRED error on the next execution and the agent is paused; the user must sign a new permit to resume.
  • Connection caps. Hitting the per-tier connection or symbol cap returns a symbol_limit error frame and closes the connection. Upgrade tier or distribute symbols across connections.

See also

Last updated: