Skip to content

Authentication

Every credential,
and when it needs you.

One balance, three ways to spend it. Two need a person once. A wallet key needs nobody: the wallet signs one line, the CREDIT activated to that wallet is the balance, and the signature is the key.

Three credentials, one balance

CredentialLooks likeWho it is forHow you get oneA person involved
Account keysk-orbio-…Your own server, script or agentThe dashboard, or orbio_create_key over MCPOnce, to sign in
Wallet keysk-orb-<epoch>-…An agent with its own walletThe wallet signs a fixed line. Nothing is issuedNever
App tokenorbio_at_…An app other people sign in toSign in with Orbio, OAuth 2.1Each person approves your app

All three are sent the same way and work on every model, tool and infrastructure route, and on MCP. Each spends one account's balance; none of them can move tokens, sign a transaction or act on a launchpad vault.

Account keys

Every Orbio account has one permanent key. Sign in at orbio.so/dashboard to copy it, or call orbio_create_key from an MCP client connected over OAuth, which returns the same key rather than making another.

  • Asking for the key again returns the live one. It is never rotated by being shown.
  • Revoking stops it at once; the next request for a key makes a new one. The balance is untouched either way.
  • It works beside a wallet key on the same account. Making one never retires the other.

Wallet keys: no sign-in

A wallet key is the wallet's own signature of one fixed line, Orbio API key · chain 4663 · epoch 0, presented as sk-orb-<epoch>-<signature in base64>. Orbio issues nothing and stores no secret: the gateway rebuilds the line from the epoch in the key and recovers the wallet that signed it. The balance is whatever CREDIT has been activated to that wallet, and its first activation gives it an account of its own.

  • A plain EOA and an EIP-7702 delegated wallet sign with their own key, and both work.
  • A contract wallet (a Safe, an ERC-4337 smart account) has no key to recover, so it cannot make a wallet key. Sign with an EOA and activate to it, or use an account key.
  • Signing in at orbio.so later with only this wallet, as a new login, opens this same account and its balance.

1. Sign the key

With the wallet the agent already has, starting at epoch 0. The text must match character for character, middle dots included, and the signature is base64 of its 65 bytes, not hex. A key that cannot be read is refused with a message naming the line it should have signed.

TypeScript · viem
import { privateKeyToAccount } from 'viem/accounts'

const wallet = privateKeyToAccount(process.env.ORBIO_PRIVATE_KEY as `0x${string}`)
const epoch = 0
const signature = await wallet.signMessage({ message: `Orbio API key · chain 4663 · epoch ${epoch}` })
const ORBIO_API_KEY = `sk-orb-${epoch}-${Buffer.from(signature.slice(2), 'hex').toString('base64')}`
TypeScript · @orbiodotso/sdk
import { createOrbio } from '@orbiodotso/sdk'

// ORBIO_PRIVATE_KEY and ORBIO_RPC_URL set, and no ORBIO_API_KEY:
// the client signs its own wallet key from that wallet.
const orbio = await createOrbio()

// Buy CREDIT with USDG, activate it to this wallet, and wait for the balance.
await orbio.topUp({ token: 'USDG', amount: '10' })
TypeScript · @orbiodotso/sdk, the key alone
import { walletKey } from '@orbiodotso/sdk'

// For another client, such as the OpenAI SDK.
const ORBIO_API_KEY = await walletKey(process.env.ORBIO_PRIVATE_KEY as `0x${string}`)
Python · eth-account
import base64, os
from eth_account import Account
from eth_account.messages import encode_defunct

epoch = 0
line = f"Orbio API key · chain 4663 · epoch {epoch}"
signed = Account.sign_message(encode_defunct(text=line), private_key=os.environ["ORBIO_PRIVATE_KEY"])
ORBIO_API_KEY = f"sk-orb-{epoch}-" + base64.b64encode(signed.signature).decode()

2. Fund the wallet

Activate CREDIT to the wallet that signed. Nobody signs in: the activation is the account. CREDIT for agents covers buying it on the order book or through Uniswap, and /api/v1/swap already takes the wallet key at this point, before anything is activated, so an agent holding only USDG, ETH or ORBIO can buy its first CREDIT. With the SDK, orbio.topUp() buys, activates and waits for the balance in one call, as above.

Solidity · three ways to activate
// From the wallet that signed the key:
credit.activate(amount);

// From any wallet, to the one that signed it:
credit.activate(amount, bytes32(uint256(uint160(agentWallet))));

// Or buy from the order book and activate in one call, after approving USDG:
exchange.buyAndActivate(usdgIn, minCreditOut, bytes32(uint256(uint160(agentWallet))), maxFills);

Amounts have 6 decimals, and previewActivation(amount) says what an activation will credit. CREDIT bridged to Ethereum activates there the same way, to the same balance.

3. Confirm the activation

The gateway learns about an activation from its indexer, a few seconds after the receipt. Poll GET https://www.orbio.so/api/protocol/tx/{hash} (add ?chain=1 for Ethereum) until status is applied. From then on the key works, and GET /api/v1/key shows the balance and the wallet it belongs to.

Check the activation, then the key
curl https://www.orbio.so/api/protocol/tx/$ACTIVATION_TX
# {"status":"applied","blockNumber":"…","events":[…,"Activated"]}

curl https://api.orbio.so/api/v1/key -H "Authorization: Bearer $ORBIO_API_KEY"
# {"object":"key","key":{"kind":"wallet","prefix":"sk-orb-0","label":null,"created_at":null,"address":"0x…"},"balance":{"currency":"USD","available":"25",…},…}

Until the activation is applied, every route that spends answers 402 wallet_not_activated with the recovered address. If that is not the wallet you funded, the key was signed by a different wallet or over different text.

4. Use it, and rotate it

Use it exactly as an account key: the same base URLs, headers and MCP endpoint, below.

Rotate by signing a higher epoch. The first request that uses it retires every lower epoch for that wallet, which then answers key_rotated. Only the private key can raise the epoch, so this is also how a leaked wallet key is stopped.

App tokens

An app that other people use never asks them for a key. It sends them to Orbio to sign in and approve it, and spends their balance with the token it gets back, within the scopes they approved. Sign in with Orbio is the full guide.

Sending a credential

As Authorization: Bearer <credential>; the gateway also takes it as x-api-key, and MCP only as Authorization. Call the gateway base directly: a redirect, such as orbio.so to www, drops the header on the way.

ClientBase URL
OpenAI-compatible SDKs and HTTPhttps://api.orbio.so/api/v1
Anthropic SDK, Claude Agent SDK, Claude Codehttps://api.orbio.so/api, because those clients add /v1/messages themselves
MCPhttps://www.orbio.so/api/mcp
OpenAI SDK
import OpenAI from 'openai'

const client = new OpenAI({ apiKey: process.env.ORBIO_API_KEY, baseURL: 'https://api.orbio.so/api/v1' })
Claude Code, with a key instead of a sign-in
claude mcp add --transport http orbio https://www.orbio.so/api/mcp \
  --header "Authorization: Bearer $ORBIO_API_KEY"

An account key and a wallet key are server credentials. The gateway answers a browser page only for an app token, so a key in browser code can be stolen and cannot even be used there.

Reading the balance

GET /api/v1/key returns the balance, lifetime usage, rate limits and, for a wallet key, the wallet it recovered to. GET /api/v1/auth/key answers in OpenRouter's shape for clients that already read limit_remaining. Every gateway response also carries X-Orbio-Balance, the balance the request started from. Money is a decimal string beside an exact integer of micro-dollars.

Refusals

Models, tools, swap, MCP and Incognito refuse a credential in one shape, { "error": { "message", "type", "code", "param", "docs_url" } }, with address added when the refusal is about a wallet. Infrastructure routes refuse an account key or a wallet key with the same code and words in their own { "error": { "code", "message", "retryable" } }, and use their own codes for anything else, such as unauthorized for no credential at all. Branch on code; the message is written to be shown to whoever has to act.

StatusCodeWhenWhat to do
401missing_api_keyNo key arrived. A redirect drops the Authorization header, so calling orbio.so instead of the gateway base looks like this too.Send Authorization: Bearer <key> (or x-api-key) to the gateway base itself.
401invalid_api_keyThe key is not one Orbio issued, or it was revoked.Use the account's live key. A revoked key never comes back; asking for a key makes a new one.
401invalid_api_keyThe key starts with sk-orb- but is not a 65-byte signature in base64. The message says which line should have been signed.Encode the signature in base64, not hex, and check the signed text character for character.
401key_rotatedAn account key was replaced, or a wallet key was signed with a lower epoch than that wallet has since used.Use the new key, or sign the key message again with a higher epoch.
402wallet_not_activatedA wallet key whose wallet has no account yet, because nothing has been activated to it. address is the wallet the signature recovered to.Activate CREDIT to address, wait until the activation is indexed, and send the same key again. If address is not your wallet, the signed text was wrong.
402insufficient_quotaThe account's balance is empty. On a tool call the same refusal is ORB03, and ORB02 when the balance cannot hold the call's quote; a private request keeps insufficient_quota in Incognito's own shape.Activate more CREDIT to the wallet, or top up the account. Nothing was charged.
403insufficient_scopeAn app's token without the scope this route needs.Ask the person to connect the app again with that scope.
503server_errorThe credential could not be checked.Retry. Nothing was charged.