Docs
API https://api.orblivion.com Home

Hosted agents

A hosted agent's loop runs on Orblivion's servers, next to the chain: on every tick it reads the market, its brain decides, and each order passes the agent's limits and safety checks before it is planned and traded. You pick a built-in brain or point it at your own webhook. It trades on paper first, and live only through a session the owner signs.

Paper and live#

PaperLive
Tradessimulated against live chain state and filled in the agent's own paper bookreal trades from the owner's wallet, through a session
Needsan API keya session granted to the agent's executor, the executor funded with a little ETH for gas, the server's live trading on, and the agent's own live switch on
Risk termsthe owner's config (accept_risks, shield, policies)the session's signed binding; the agent's config must sit inside it
Feesthe fee schedule for its Shield settingthe session binding's fees (see Fees)
Startsactive at oncepaused until every step below is done

Every order passes the same checks either way: the agent's limits, the market guards, the policy and the risk terms.

Create an agent#

POST /api/v1/hosted with the owner's API key. Each wallet may have up to 20 agents.

JSON
{
  "name": "eth-dca",
  "brain": "dca",
  "params": {"sell": "USDG", "buy": "ETH", "amount_usd": 10, "period_s": 3600},
  "interval_s": 60,
  "pairs": ["ETH-USDG"],
  "limits": {"max_trade_usd": 20, "max_day_usd": 60, "max_position_usd": 200},
  "mode": "paper"
}
FieldRules
brain, paramsone of the brains below, with its own parameters
interval_sseconds between ticks, from 15 to 86,400
pairs1 to 6 pairs: "ETH-USDG" style for the core four (ETH, USDG, cbBTC, ORBIO), "0x<token>-USDG" for any other token by address. A Robinhood stock token only for a live agent (Stock tokens), and WETH is named ETH.
limitsmax_trade_usd (1 to 1,000), max_day_usd (from max_trade_usd to 100 times it), max_position_usd (the most it may hold in any token but USDG)
modepaper (default) or live
nameup to 40 letters, digits, spaces, dots, dashes and underscores
paper_booka paper agent's starting balances (default 1,000 USDG and 0.4 ETH)
accept_risks, shield, policiesthe agent's risk terms, as on any trade

Any field not listed is refused (UNKNOWN_FIELD). A token by address must be in the registry, have a route and have been screened within 26 hours.

Brains#

brainWhat it doesparams
dcabuys a fixed dollar amount every periodsell, buy, amount_usd, period_s, optional max_buys
rebalancetrades back to target weights when they drifttargets ({"ETH": 0.5, "USDG": 0.5}, summing to 1, with USDG), optional threshold_bps and max_trade_usd
conditionallimit buys and sells, stop-losses and take-profits, confirmed before they fireorders (1 to 10 of {id, kind, base, trigger_price, amount_usd}, kind one of limit_buy, limit_sell, stop_loss, take_profit), optional confirm_ticks (2), confirm_bps (10), max_slippage_bps (100), min_liquidity_usd (10,000)
momentum, mean_reversionsimple trend and reversion strategies on the hosted market datanone
webhookyour own HTTPS endpoint decides (below)url
orbio_llman AI model on Orbio decides from your own strategy prompt (AI agents below)strategy, model, funding, optional template, budget_usd_day (0.25), max_orders (2)

A conditional order fires only when its trigger holds on the live quote for confirm_ticks ticks in a row and a second source agrees (Chainlink, or the median of the best routes), so one pushed block can't trigger a stop-loss. Every order also gets an on-chain minimum from the engine's own fresh quote less the agent's slippage.

Going live#

  1. Create the agent with "mode": "live". It comes back paused, with an executor address and an executor_attestation.
  2. Check the attestation. The executor is a key Orblivion's signing service made for this owner and this agent; it never leaves that service. The attestation is the co-signer's EIP-191 signature over
Text
   pit.hosted.executor.v1
   chain:4663
   owner:<owner address, lowercase>
   agent:<agent id>
   executor:<executor address, lowercase>

Recover it and check it is the pinned co-signer 0x75211e73ceaf6f647783293258f0d6bc6a63e780 before granting anything. The SDKs do this (EXECUTOR_ATTESTATION).

  1. Grant a session to the executor with the main key (Sessions). For a hosted agent the root must carry the on-chain ETH day cap when the pairs include ETH, the ERC-20 budget must be at most one day's cap, the agent's limits and risk terms must sit inside the session's, and a session with a tier C token lasts at most 24 hours.
  2. Attach it: POST /api/v1/hosted/<id>/session with {"session_id": "ses_..."}. A session that doesn't fit is refused with every reason (BAD_SESSION, OUTSIDE_SESSION).
  3. Fund the executor with a little ETH for gas. GET /api/v1/hosted/<id>/executor gives its balance and the minimum it needs (about ten trades' worth). Keep it small: while the agent runs, the executor sends only the agent's own trades. Once you stop the agent, what is left comes back to your wallet with POST /api/v1/hosted/<id>/refund-gas (below).
  4. Switch it on: POST /api/v1/hosted/<id>/update with {"live": true}, then POST /api/v1/hosted/<id>/resume.

The SDKs do steps 1 to 4 in one call:

const { agent, grant } = await owner.hosted.create({
  brain: 'dca', params: { sell: 'USDG', buy: 'ETH', amount_usd: 10, period_s: 3600 },
  intervalS: 60, pairs: ['ETH-USDG'],
  limits: { maxTradeUsd: 20, maxDayUsd: 60, maxPositionUsd: 200 },
  mode: 'live', session: { maxTrades: 50, hours: 24 }, allow7702: true,
})
const gas = await owner.hosted.executor(agent.id)    // send a little ETH to gas.executor
await owner.hosted.update(agent.id, { live: true })
await owner.hosted.resume(agent.id)

AI agents#

An orbio_llm agent asks an AI model on Orbio's gateway what to do on each tick. You write the strategy in your own words; the model reads it with the agent's market view, book, recent fills and its own notes from earlier ticks, and answers with its reasoning and at most max_orders orders. Orblivion checks every answer before anything trades, and the orders then go through the same limits, guards and risk terms as any other brain's.

JSON
{
  "brain": "orbio_llm", "interval_s": 900, "pairs": ["ETH-USDG"],
  "limits": {"max_trade_usd": 20, "max_day_usd": 60, "max_position_usd": 200},
  "params": {
    "strategy": "Follow the trend on my pairs. Keep at least 40% in USDG; trade small.",
    "model": "anthropic/claude-haiku-5.5",
    "funding": "orblivion",
    "budget_usd_day": 0.25,
    "max_orders": 2
  }
}
FieldRules
strategyyour prompt: 1 to 1,500 characters, at most 40 lines, no control or direction-override characters. It is sent to the model as your words, after Orblivion's fixed rules, and can't change them.
templatetrend, mean_reversion_stop, dca_timing or custom: where the strategy started (the templates are plain, editable prompts; GET /api/v1/hosted/ai lists them)
modelone of the server's models (GET /api/v1/hosted/ai lists them with their prices and an estimate per tick)
fundingwho pays for the model: orblivion (the default: Orblivion's own Orbio credits, your fair daily share) or own (your own Orbio balance only, after you sign in with Orbio)
fallback_ownwith orblivion: when your share or the day's pool is used up, continue on your own Orbio balance if you signed in (default true); false waits for the next day
budget_usd_daythe most this agent's model calls may cost in a UTC day: $0.01 to $20. Once spent, the agent keeps ticking but doesn't call the model (and so doesn't trade) until the next day.
max_ordersat most this many orders in one answer, 1 to 3 (a live agent still makes at most one trade a tick)
interval_sat least 300 for an AI agent: each tick is a paid call

Who pays. By default Orblivion does, from its own Orbio credits: a daily pool sized from what Orblivion's own Orbio account earns and holds, always keeping 30 more days of it in reserve, shared fairly. Each wallet gets one share a day, whatever its agents: agents that trade live through Orblivion split the pool (at most $0.50 a wallet), once the wallet has made at least $5 of trades through Orblivion over the last 14 days and has an active trading session, and each wallet's share is backed by the trading fees it paid through Orblivion over those 14 days: at most 0.07 times them a day (about the fees it pays a day; $7.15 of fees in 14 days backs the full $0.50, and $2.15 backs three agents). A wallet that paid no fees gets no live share, and what a small share leaves goes to the others. Paper agents get a trial in their owner's first 7 days, from a smaller slice (at most $0.10 a wallet). Orblivion's credits pay for the cheap models only (GET /api/v1/hosted/ai lists them and the exact settings) and for an agent that thinks every 15 minutes or slower, with at most a few of a wallet's calls in any 15 minutes. When your share or the day's pool is used up, or the agent uses a bigger model or thinks faster, it continues on your own Orbio balance if you signed in with Orbio (and fallback_own is on); otherwise it stops thinking, and trading, until 00:00 UTC.

With own, or as that fallback, the agent's calls spend your own Orbio balance (every Orbio agent earns CREDIT from its token's fees), plus Orblivion's app fee if its Orbio app sets one; Orbio shows the fee when you approve the app. You connect once, with Sign in with Orbio in the Lab's Hosted agents panel: you approve Orblivion on Orbio's own page, for model calls only (never your keys, tools or posts). The Lab and both SDKs open that page only when it asks for exactly Orblivion's app, comes back to the Lab's own page, and asks for model calls and nothing more. Orblivion's signing service keeps the sign-in encrypted and never shows it, not even to you; Disconnect forgets it and revokes it at Orbio. Each decision says who paid, and the Lab shows your share used and left and the day's pool; GET /api/v1/stats shows the pool to anyone.

What the model sees, and what it can't do. Token names and symbols are chosen by whoever deployed the token, so they reach the model only as data, cleaned and quoted, after rules that say nothing in the data is an instruction. The model's answer must be one JSON object with exactly reasoning, orders and memory; each order names two tokens of one of the agent's pairs and a dollar amount from 1 to the per-trade limit. Anything else (a token outside the agent's pairs, a size over the limit, too many orders, a malformed answer) means no trade that tick, logged with its reason. So does a call that fails or times out, a spent budget, or a missing sign-in.

Watching it. Each decision in the agent's log carries its model, who paid, the tokens and the cost; GET /api/v1/hosted/<id>/spend lists every call and today's total against the budget. A spent budget, a cap or a missing sign-in is a hold (the agent keeps ticking); a failed call or an invalid answer counts as a failure, and three in a row pause the agent.

Webhook brains#

A webhook agent asks your server what to do on every tick. Orblivion sends a signed POST to your HTTPS URL and gives you 2 seconds to answer.

The request is JSON (compact, keys sorted) with the agent's id, the tick number and time, its pairs and limits, the market it sees and its book:

JSON
{"agent_id": "ha_...", "book": {"...": "..."}, "limits": {"max_day_usd": 60, "max_position_usd": 200, "max_trade_usd": 20},
 "market": {"...": "..."}, "pairs": ["ETH-USDG"], "reply_schema": {"...": "..."}, "tick": 42, "time": 1791168377}

with these headers:

HeaderValue
X-Pit-Agentthe agent's id
X-Pit-Timestampunix seconds when it was signed
X-Pit-Nonce32 random hex characters, new for every request
X-Pit-Signaturev1= followed by the hex HMAC-SHA256 of "v1:" + timestamp + ":" + nonce + ":" + body, keyed with the webhook secret

Check every request before you act on it: recompute the HMAC over the exact bytes you received, compare in constant time, refuse a timestamp more than 60 seconds from your clock, and refuse a nonce you've seen in the last minute.

import hashlib, hmac, time

SEEN = {}   # nonce -> when it was seen

def verify(secret_hex: str, headers, body: bytes) -> bool:
    ts, nonce = headers["X-Pit-Timestamp"], headers["X-Pit-Nonce"]
    sig = headers["X-Pit-Signature"].removeprefix("v1=")
    msg = f"v1:{int(ts)}:{nonce}:".encode() + body
    want = hmac.new(bytes.fromhex(secret_hex), msg, hashlib.sha256).hexdigest()
    now = time.time()
    for n, t in list(SEEN.items()):
        if now - t > 60:
            del SEEN[n]
    if not hmac.compare_digest(want, sig) or abs(now - int(ts)) > 60 or nonce in SEEN:
        return False
    SEEN[nonce] = now
    return True

The reply must be exactly this JSON, or the tick holds:

JSON
{"reasoning": "ETH is under its 20-day mean; adding a little.",
 "orders": [{"sell": "USDG", "buy": "ETH", "amount_usd": 10, "reason": "dca"}]}
  • reasoning: 1 to 1,200 characters. orders: at most 3, each with sell, buy and amount_usd, and an optional reason (up to 280 characters). Nothing else.
  • Orders may name only the agent's own pair tokens. An order naming anything else is held.
  • Your reply is never trusted beyond the rules: its orders pass the same limits, guards, policy and risk terms as any brain's.

The URL must be https:// with no credentials, and its host must resolve only to public addresses (the connection is pinned to the address checked, so DNS can't be switched under it). Redirects aren't followed, and the answer may be at most 64 KB.

The secret is shown once, as webhook_secret in the answer to create. Store it on your server. POST /api/v1/hosted/<id>/rotate-webhook-secret makes a new one, shown once; the old one stops at once. Orblivion's API servers never hold the secret: a separate signing service keeps it encrypted and signs each request.

The kill switch and pauses#

  • The owner can pause, resume, stop (final: it also revokes the agent's session at Orblivion and returns the on-chain revocation to send) and delete (a live agent must be stopped first).
  • Two switches for live trading: the server's, and the agent's own live. While either is off a live agent still decides each tick, and logs it (LIVE_OFF), but doesn't trade. An agent's trading field says what it is doing now: live, paper or not trading.
  • The kill switch: Orblivion's operators can pause every live agent at once. It is checked again before co-signing, before the executor signs and before relaying, and releasing it resumes nothing: each owner resumes their own agents. A paused agent's pause_reason says why, and resume answers 409 KILLED while the switch is engaged.
  • Automatic pauses: low executor gas, three failures in a row (a reverted transaction counts), the session ended or within 10 minutes of its end, the daily limit or the session's caps reached, the kill switch, the server's live switch off. The reason is in the agent's pause_reason.
  • Fail closed. An order is held, never sent, when the signing service doesn't answer, the market data is stale, two RPC providers disagree, the quote is too far from the reference, liquidity is too thin, gas is too dear, the limit can't be met, or the session is near a cap.
  • Restart-safe. A tick runs once per slot, and a signed transaction is stored before it is relayed, so a crash never trades twice.

Risk terms for agents#

  • A paper agent trades with its owner's config. A live agent trades with its session's signed binding, and its config must sit inside it (OUTSIDE_SESSION).
  • Terms never come from a brain, a webhook or an order. An order whose risks the terms don't accept is skipped and logged, not counted as a failure.
  • With Shield, the tokens a live agent holds are watched every 15 minutes and every tick: if Dossier comes to call one an impersonator, the agent stops trading that token's pairs and the owner is told once (HELD_IMPERSONATOR).

Fees#

  • A paper agent pays the fee schedule for its Shield setting: 0.25% on a major trade, 0.50% on any other, and 0.10% more with Shield.
  • A live agent pays its session binding's fees (Sessions).
  • The agent's terms and session_terms carry fees ({major_max_bps, major_min_bps, other_bps}) and fee_schedule: 2, or 1 for a session granted before schedule 2, which also keeps its fee_bps.
  • Each trade in the agent's logs records the fee it paid: fee_bps, fee_class and fee_tier.
  • The agent reads each order's class itself. It holds an order whose plan's class isn't its own reading (FEE_CLASS), or whose fee its terms don't allow (FEE_MISMATCH), and logs why.

Limits and gas#

LayerCaps
The agentper trade, per 24 hours, the largest position in any token but USDG, one live trade per tick, ticks at least 15 seconds apart
The sessionper trade, per 24 hours, the number of trades and the expiry; on chain and at the co-signer
Gasthe executor's transactions are capped per transaction and per 24 hours; the agent holds while gas is unusually expensive and pauses when the executor runs low

Watching an agent#

  • GET /api/v1/hosted/<id>: status, mode, whether it is trading, its brain, P&L, last decision, its tokens with their tier and risks, its terms and its session's session_terms (each with its fees).
  • GET /api/v1/hosted/<id>/logs?limit=50&before=<id>&kinds=trade,hold: every decision, hold and trade, newest first, with each step's time and each trade's fee. Page back with next_before.
  • GET /api/v1/hosted/<id>/executor: the executor's ETH and whether it is enough.
  • POST /api/v1/hosted/<id>/rotate-executor: a new executor key, if you think the old one is exposed. The agent's session is revoked at Orblivion and it pauses until you grant and attach a new one.

Stock tokens#

Robinhood's stock tokens aren't offered to US persons. A live agent may trade them once its owner signs a non-US attestation for the agent's session, the same statement an own-key trade carries, but signed by the owner wallet so the agent can rely on it while you're away:

Text
pit.hosted.stock.v2
chain:4663
owner:<owner address, lowercase>
agent:<agent id>
executor:<executor address, lowercase>
session:<session id>
root:<the session root's hash, lowercase>
issued:<unix seconds, when you sign it>
expires:<unix seconds>
statement:I am not a US person and I am not acting for one. This agent may trade Robinhood stock tokens for this wallet through this session until the expiry above.
  • Add the stock token's pair by address ("0x<stock token>-USDG") to a live agent and to its session; a paper agent can't name one (TOKEN_STOCK).
  • Sign the attestation with the owner wallet (EIP-191 personal_sign) and send it: POST /api/v1/hosted/<id>/stock-attestation with {session_id, issued, expires, signature}. The Lab's "Attest non-US (stock tokens)" button, hosted.attest_stock(id) in Python and hosted.attestStock(id) in TypeScript build the text, check the executor is attested by Orblivion's signing service, and sign it. GET /api/v1/hosted/<id> gives session_root_hash and session_expires to build it from, and stock_attestation_not_before.
  • issued is when you sign it: within the last hour, and after the agent's last attestation and last revocation (at least stock_attestation_not_before). So an attestation is taken once: one you replaced or revoked can't be handed in again, by anyone. expires is at least 5 minutes away and no later than the session's end or 30 days. Orblivion checks the signature, then its signing service checks it again against its own record of the session and keeps it; it co-signs a stock trade for the agent's executor only under an attestation that holds.
  • The agent then trades stock tokens within the session's limits while the US stock-token session is open (Sunday 20:00 to Friday 20:00, New York). Its stock orders are held, never failed, outside those hours (MARKET_CLOSED) and without a valid attestation (STOCK_ATTESTATION).
  • A new session, a new executor or the expiry ends it: sign again. POST /api/v1/hosted/<id>/stock-attestation/revoke takes it back at once, for good: only one you sign afterwards is taken. The agent shows it as stock_attestation (valid, why_not, issued, expires), and the tokens it found to be stock tokens on chain as stock_tokens.
  • A trade co-signed under an attestation just before it was revoked or ran out can still execute for up to two minutes, as any co-signed trade.

Getting the executor's gas back#

When you stop a live agent, the ETH left on its executor is still yours. POST /api/v1/hosted/<id>/refund-gas (the Lab's "Refund executor gas" button, hosted.refund_gas(id) in Python, hosted.refundGas(id) in TypeScript) sends it back:

  • Orblivion's signing service builds one plain ETH transfer from the executor to the wallet it recorded as the agent's owner when it made the key. Nothing in the request can name another recipient or an amount.
  • The amount is the executor's balance less that transfer's own gas limit times its maximum fee, read by the signing service from its own RPCs. The few wei of gas it doesn't use stay on the executor.
  • It needs the agent stopped (or deleted) and every session you granted its executor revoked or ended. It then closes the executor for good (no session, co-signature or trade for it again) and waits up to two minutes for anything already co-signed to expire, so no trade can race it. A deleted agent's refund works the same way.
  • Only the agent's owner can grant its executor a session (EXECUTOR_OWNER), so no one else's session can hold up your refund.
  • Asked again before it is mined, the same signed transfer comes back, so at most one refund can land. If more ETH arrives later, ask again once the first is mined.
  • The SDKs check the answer is a plain transfer from the executor to your own wallet (REFUND_TO).