Cascade
Testnet

Connect to API

Authenticate an owner account with an authorized signer and obtain JWT credentials for REST and WebSocket

Connect to API

Use this guide to authenticate with Cascade and get a JWT bearer token for REST and WebSocket access.

Requirements

Before continuing, complete:

  1. Setup Account
  2. Setup Delegate

Authenticate with the server

Use the widget below to quickly get an API key for your owner account. The signer can be the owner wallet or a delegate that has already been authorized for that owner account. Once authenticated, you can test the API here.

No API key

Identity mapping

Use these terms consistently across REST and WebSocket calls:

RoleExampleWhere it belongs
Owner account0xAa...01The stable trading account identity. Pass this to GET /auth?account=..., REST /account/* routes, and the intended private WS account field.
Delegate signer0xBb...02The wallet that signs the auth challenge or EIP-712 payloads when delegation has been granted for the owner account + subaccount.
Subaccount index0The risk sleeve inside the owner account. Pass this anywhere a private route or channel asks for subaccountIndex.

Manual authentication steps

1. Request a challenge

curl "https://engine.cascade.cooking/auth?account=<OWNER_ACCOUNT>"

2. Sign and POST proof

  • Sign signing_payload using EIP-191 (eth_sign / personal sign)
  • The signer can be the owner wallet or a delegate wallet that has already been authorized for the target owner account
  • POST { message, signature, server_signature } back to /auth
  • Store returned { token, claims }

3. Use JWT for REST

Authorization: Bearer <token>

4. Authenticate WebSocket

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "auth",
  "params": { "bearer": "<token>" }
}

JavaScript example (viem)

import { createWalletClient, custom } from "viem";

const ethereumProvider = /* browser wallet, WalletConnect, embedded signer, etc. */;
const wallet = createWalletClient({ transport: custom(ethereumProvider) });

const ownerAccount = "0x..."; // Cascade owner account
const [delegateAddress] = await wallet.getAddresses();

const challenge = await fetch(
  `https://engine.cascade.cooking/auth?account=${ownerAccount}`,
).then((res) => res.json());

const signature = await wallet.signMessage({
  account: delegateAddress,
  message: challenge.signing_payload,
});

const session = await fetch("https://engine.cascade.cooking/auth", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    message: challenge.message,
    signature,
    server_signature: challenge.server_signature,
  }),
}).then((res) => res.json());

const token = session.token;

await fetch("https://engine.cascade.cooking/account/orders", {
  headers: { Authorization: `Bearer ${token}` },
});

Python example (web3.py)

import os
import requests
from eth_account import Account
from eth_account.messages import encode_defunct

BASE_URL = "https://engine.cascade.cooking"
OWNER_ACCOUNT = os.environ["OWNER_ACCOUNT"]
delegate = Account.from_key(os.environ["DELEGATE_PRIVATE_KEY"])

challenge = requests.get(
    f"{BASE_URL}/auth",
    params={"account": OWNER_ACCOUNT},
    timeout=10,
).json()

message = encode_defunct(text=challenge["signing_payload"])
signature = Account.sign_message(message, private_key=delegate.key).signature.hex()

session = requests.post(
    f"{BASE_URL}/auth",
    json={
        "message": challenge["message"],
        "signature": signature,
        "server_signature": challenge["server_signature"],
    },
    timeout=10,
).json()

token = session["token"]

requests.get(
    f"{BASE_URL}/account/orders",
    headers={"Authorization": f"Bearer {token}"},
    timeout=10,
)