DEVELOPER GUIDE · VERIFIED AGAINST THE RUNNING API
Connect your AI agent
Any HTTP-capable agent can read IOST Terminal market data with no credentials at all. To act on an account, the owner mints a scoped itk_ key, and the agent sends it as X-API-Key, exchanges it for an OAuth 2.0 bearer token, or speaks MCP to /mcp. Paper trading is the default lane. Real-money trading is off by default, lives behind an owner approval queue, and can never be authorized by an agent key.
1. Mint a scoped agent key
Keys are minted by the signed-in owner, inside the app, at Portfolio → AI Agents → Create key. The window does the same thing the API does:
| Method and path | What it does |
|---|---|
POST /api/agent-keys | Mint a key. Body: name, scopes, optional expiresAt (future Unix epoch milliseconds). |
GET /api/agent-keys | List your keys. Returns the id, name, a short display prefix, scopes and timestamps — never the secret. |
DELETE /api/agent-keys/:id | Revoke a key. Effective immediately, including every bearer token derived from it. |
What the key is and is not:
- The secret is prefixed
itk_and is shown exactly once, at creation. Only a SHA-256 hash is stored, so nobody — including us — can read it back. If it is lost, revoke it and mint a replacement. - New keys are read-only by default.
- There is no withdrawal scope and no administrator scope. An agent can never mint its own replacement key.
- Revocation or expiry does not cancel an order that was already submitted.
The three scopes are the whole vocabulary. read is always granted. trade-paper may open and close paper trades. trade-live is owner-only: it can be minted only by a signed-in human whose own account is already live-admissible, and even then it only lets the agent request live orders — it never authorizes one.
Mint a key with the owner's session cookie:
curl -s -X POST https://iostcallister.com/api/agent-keys \
-H 'content-type: application/json' \
-b 'iost.sid=YOUR_SIGNED_IN_SESSION' \
-d '{"name":"my-agent","scopes":["read","trade-paper"]}'
The response contains the secret once, plus a warning to store it now. List what you already have:
curl -s https://iostcallister.com/api/agent-keys \
-b 'iost.sid=YOUR_SIGNED_IN_SESSION'
2. Authenticate — three supported options
Option A — the API key header (simplest)
Send the key as X-API-Key on every request. It resolves to the owner's identity narrowed by the key's scopes.
curl -s https://iostcallister.com/api/agent-keys \
-H 'X-API-Key: itk_EXAMPLE_NOT_A_REAL_KEY'
Option B — OAuth 2.0 client_credentials
Exchange the key for a short-lived opaque bearer token at POST /oauth/token. client_id is the key's id (shown in the app), client_secret is the full itk_ secret, and resource must be either the API root or the MCP endpoint. Tokens last 15 minutes, are audience-bound, and are retained only as a SHA-256 digest in memory. An optional scope parameter can only narrow the source key — it can never add a scope.
curl -s -X POST https://iostcallister.com/oauth/token \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' \
-d 'client_id=KEY_ID_FROM_THE_APP' \
-d 'client_secret=itk_EXAMPLE_NOT_A_REAL_KEY' \
-d 'resource=https://iostcallister.com/mcp'
Then send Authorization: Bearer <access_token>. Revoke a token early with POST /oauth/revoke, or revoke the source key to invalidate every token derived from it at once. Discovery lives at /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource.
Option C — MCP over streamable HTTP
The MCP endpoint is POST https://iostcallister.com/mcp. Anonymous callers get the public, read-only tool set; a user-bound key or bearer token adds the private evaluation and paper-execution tools allowed by its scopes. MCP sessions always exclude trade-live. No MCP tool can execute a live, token, conversion, swap, wallet-send or public-chain action.
Two protocol revisions are accepted: the current 2026-07-28 and the legacy 2025-06-18. Send the revision in the mcp-protocol-version header. Under the current revision the flow starts with server/discover and initialize is deliberately not implemented; under the legacy revision initialize is the handshake:
curl -s -X POST https://iostcallister.com/mcp \
-H 'content-type: application/json' \
-H 'mcp-protocol-version: 2025-06-18' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"my-agent","version":"1.0"}}}'
Then list the tools you are actually allowed to call, and read their input schemas before using them:
curl -s -X POST https://iostcallister.com/mcp \
-H 'content-type: application/json' \
-H 'mcp-protocol-version: 2025-06-18' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
Authenticated requests use the same header or bearer token as the REST API. Tool annotations are hints only — the server re-checks authorization at execution time, so never assume a tool name, count or permission from documentation.
3. Read the machine-readable contract
These documents are served by the same process as this page and are the authoritative description of the surface:
| Document | Contents |
|---|---|
| /auth.md | Authentication: key minting, scopes, OAuth, MCP, the live proposal rail, fail-closed rules. |
| /openapi.json | OpenAPI specification: routes, security schemes (ApiKey, BearerAuth, OwnerSession) and scopes. |
| /.well-known/mcp/server-card.json | MCP server card: endpoint, supported protocol revisions, capabilities, safety posture. |
| /.well-known/ai-catalog.json | AI catalog (ARD) entry describing the service to agent runtimes. |
| /.well-known/agent-skills/index.json | Agent Skills index, including the agent-auth skill. |
| /llms.txt | Plain-language quick guide for language models. |
Public market data (/api/ui-state, /api/scores, /api/scanner, /api/analyze/:symbol, /api/news, /api/onchain and others) needs no credentials at all. Start there.
4. Respect the safety model
This is the part that decides whether your agent is useful or dangerous. Every rail below is enforced server-side, not by convention.
Live orders from an agent go through the human approval queue
An agent never executes a live order. With a trade-live key it submits a proposal, and the owner approves or rejects it before anything reaches a venue:
| Step | Endpoint | Who |
|---|---|---|
| Submit a proposal | POST /api/live/proposals | Agent (needs trade-live) |
| Owner reviews the evidence | POST /api/live/proposals/:id/review | Owner session |
| Approve or reject | POST /api/live/proposals/:id/approve · POST /api/live/proposals/:id/reject | Owner session |
Direct live submission from an agent is refused; the only path is an exact, unexpired, owner-reviewed proposal. Approval never bypasses a current execution gate, and live admission itself is operator-side — there is no HTTP route that can admit an account.
Wallet Pacts are time-boxed
Agent paper spending requires a wallet-bound Pact that the owner creates and can revoke. A Pact has a hard ceiling of 720 hours (30 days); the default window when one is created is also 720 hours. The service publishes both the ceiling and a 48-hour pre-expiry warning window, and the UI reads those values from the API rather than hardcoding them. A Pact that lapses silently stops agent paper trading until the owner re-authorizes it — so any surface that shows a Pact must show the time remaining.
Hard limits
| Limit | Value | Applies to |
|---|---|---|
| Maximum notional per opening order or position addition | USD 10,000 | Paper and live |
| Maximum notional per live order | USD 10,000 (default and owner-approved ceiling) | Live lane |
| Maximum concurrent live positions | 3 (default and canary ceiling) | Live lane |
| Daily loss halt | USD 500 loss on open live positions (default and ceiling) | Live lane |
| Protective stop | Required on every live order, placed at the exchange with that order (0.2–25% below entry) | Live lane |
| Minimum cash buffer | USD 10 maintained (default, floor 5) | Live lane |
Paper opens are stricter than "under the cap": an agent needs the trade-paper scope, a positive entry and size, a protective stop, a wallet it owns, an active wallet-bound Pact, and a passing server-side portfolio-risk preflight. Closing existing exposure is not blocked by the opening-order cap. A successful preflight is not owner approval.
Live scope and admission
- Live admission is account-bound, never a global switch. Each account carries its own residency record and its own sanctions-screening record, and fails closed when incomplete, unverified, stale, unscreened, matched or inconclusive. A self-declaration never admits an account.
- The live lane is Kraken spot BTC/USD long only, placed with your own venue API key. The equities lane is research and paper only.
- The platform venue key stays platform-owner only. Use a dedicated venue key with withdrawal permission disabled.
- Trade-live key minting and live triggers are gated on that account's own verdict — and agent keys and bearer principals can never authorize live trading.
5. A sane first integration
- Read /llms.txt and /auth.md, then fetch /openapi.json.
- Call public read-only routes with no credentials and confirm your client parses them.
- Have the owner mint a
readkey in Portfolio → AI Agents and connect with that only. - Call
tools/listand read the real input schemas before asking fortrade-paper. - Move to paper with a bounded wallet and an active Pact, watching the Pact window as it counts down.
- Only then ask the owner to consider live — knowing that every live order still waits for their approval.
itk_ key like a wallet seed. If it may have leaked, revoke it in the app immediately.