> For the complete documentation index, see [llms.txt](https://docs.strikelabs.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.strikelabs.app/api-reference/session-keys.md).

# Session keys

A wallet prompt per order is fine for a transfer and unusable for trading. So the wallet signs **once** — approving a keypair you generated and an expiry you named — and every order after that is signed by that key, with no wallet interaction.

The venue still holds a signature over every order it matches. What changes is *which key* signed it.

If you have used Hyperliquid's API wallets (agent wallets), this is the same shape.

## What a session key can and cannot do

|                                                                            |   |
| -------------------------------------------------------------------------- | - |
| Place orders, cancel, and flip margin for the one account that approved it | ✅ |
| **Request a withdrawal** — to that account's own linked address only       | ✅ |
| Approve another session key                                                | ❌ |
| Choose where a withdrawal is paid                                          | ❌ |
| Move positions to another account                                          | ❌ |
| Outlive the expiry its own approval named                                  | ❌ |

The maximum lifetime is **30 days**. A leaked key is only as dangerous as the time left on it.

> **The withdrawal row is a correction.** This table previously said a session key could not withdraw. That is true in `paper` mode and **false in `deposits` mode**, where `Strike withdraw v1` is a session-signed envelope that exists precisely so a session key can request one. What bounds the loss is the destination, not the authority: the venue pays only the account's own linked wallet address, and no address in the request or the envelope can change that.
>
> Note that the approval text a wallet displays (below) still reads *"It cannot withdraw funds"*. A user signs that text, so correcting it is the operator's call rather than a documentation edit — it is recorded for them. Treat a session key as an API secret **with withdrawal rights**.

## 1. Generate a keypair

Any secp256k1 keypair. The private half never leaves your machine and is never sent to Strike — the venue only ever learns the key's **address**.

```javascript
import { secp256k1 } from "@noble/curves/secp256k1";
import { keccak_256 } from "@noble/hashes/sha3";

const privateKey = secp256k1.utils.randomPrivateKey();
const pub = secp256k1.getPublicKey(privateKey, false);          // uncompressed, 65 bytes
const sessionKey = "0x" + Buffer.from(keccak_256(pub.slice(1)).slice(12)).toString("hex");
```

## 2. Approve it with your wallet

Build this message **exactly** — the venue rebuilds it from the fields you post and recovers the signer, so a single differing byte recovers a different address and the approval is refused.

```
Strike: approve a trading session.

This lets this device place orders on your behalf until it expires.
It cannot withdraw funds, move positions, or approve another session.

Account: 0xyouraddress
Session key: 0xsessionkeyaddress
Expires: 1756404800000
```

Both addresses are **lowercased**. `Expires` is milliseconds since the epoch.

Sign it with EIP-191 `personal_sign` from the account it names, then:

```bash
curl -X POST https://api.strikelabs.app/v1/auth/session \
  -H 'content-type: application/json' \
  -d '{"address":"0xYourAddress","sessionKey":"0x…","expiresAt":1756404800000,"signature":"0x…"}'
```

```json
{
  "ok": true,
  "sessionKey": "0x…",
  "expiresAt": 1756404800000,
  "token": "…",
  "account": { "cash": 10000, "equity": 10000, "positions": [] }
}
```

This is self-authenticating: no bearer token, no prior sign-in. The signature *is* the authority. A wallet that has never called `/v1/auth/verify` gets an account opened and funded here, exactly as signing in would.

The `token` it returns is an ordinary bearer token for the read paths (`/v1/me`, `/v1/fills`) — see [Authentication](/api-reference/authentication.md). Order flow does not use it.

## 3. Sign each order

Build the envelope from the fields you are about to send:

```
Strike order v1
account: 0xyouraddress
series: GOOGL-20260824-345-C
side: buy
qty: 1000000
price: 2500000
nonce: 1755800000123
```

* `account` is the **approving wallet**, lowercased — not the session key.
* `qty` and `price` are **integer micros** (multiply by 1,000,000). Decimals are not signable: `2.5` and `2.50` are the same number and different strings.
* `price` is the literal word `market` for a market order. Note that `price: 0` is a real limit price and a different envelope.
* `nonce` is milliseconds since the epoch, and must be within **60 seconds** of venue time. It may not repeat on the same key.
* No field may contain a newline. `series` is the only free-form one; the venue refuses to encode a newline rather than let one envelope stand for two orders.

Sign it with the **session key** — again EIP-191 `personal_sign` — and send the signature alongside the order:

```bash
curl -X POST https://api.strikelabs.app/v1/order \
  -H 'content-type: application/json' \
  -d '{
    "seriesId": "GOOGL-20260824-345-C",
    "side": "buy",
    "qty": 1,
    "price": 2.5,
    "sessionKey": "0x…",
    "nonce": 1755800000123,
    "signature": "0x…"
  }'
```

The venue rebuilds the envelope from the values it is **about to match on** — not from your string — so a signature cannot cover one order and be spent on another. Change the price, size, side or series and it no longer verifies.

No `x-paper-token` header is needed on a signed order.

## Listing and revoking

```bash
curl "https://api.strikelabs.app/v1/auth/session?address=0xYourAddress"
```

```json
{ "sessions": [{ "sessionKey": "0x…", "expiresAt": 1756404800000, "approvedAt": 1755800000000 }] }
```

Revoking takes the same proof as approving — a wallet signature over the approval message the key was granted under, with its original `expiresAt`. A bearer token is deliberately not enough: otherwise anyone who learned a key address could switch off its owner's trading.

```bash
curl -X POST https://api.strikelabs.app/v1/auth/session/revoke \
  -H 'content-type: application/json' \
  -d '{"address":"0xYourAddress","sessionKey":"0x…","signature":"0x…"}'
```

## Errors

All `401` unless noted. See [Error responses](/api-reference/errors.md).

| `error`                                           | What happened                                                                                                                                                                                                  |
| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bad signature`                                   | The envelope you signed is not the one the venue rebuilt. Check micros, lowercasing, and field order.                                                                                                          |
| `approval was not signed by the account it names` | The approval recovered to a different address than `address`.                                                                                                                                                  |
| `expiry must be in the future and within 30 days` | `expiresAt` is past, or more than 30 days out.                                                                                                                                                                 |
| `unknown or revoked session key`                  | Never approved, revoked, or swept after expiry.                                                                                                                                                                |
| `session expired — approve a new one`             | The key is past its `expiresAt`.                                                                                                                                                                               |
| `nonce outside the accepted window`               | More than 60s from venue time — usually a clock problem.                                                                                                                                                       |
| `nonce already used`                              | Replay. Use a fresh millisecond value.                                                                                                                                                                         |
| `field contains a newline`                        | A `seriesId` with a newline in it.                                                                                                                                                                             |
| `too many orders in flight — retry`               | **429**, not 401. More nonces in flight on this key than the venue will remember. The session is valid — retry; do not re-approve. Takes tens of thousands of signed requests inside one millisecond to reach. |

Nonces are remembered for the freshness window in both directions, not for a fixed count of requests, so a burst can never push an earlier nonce out of memory while that nonce is still live. A captured request body is replayable never.

## Signing other mutations

`/v1/cancel` and `/v1/margin` take the same three fields (`sessionKey`, `nonce`, `signature`) over their own envelopes. Each names its operation on the first line, so a signature taken for one kind of request can never be spent on another.

```
Strike cancel v1
account: <lowercase 0x address>
symbols: <UPPERCASE, comma-joined, sorted — or the word `all`>
nonce: <ms since epoch>
```

```
Strike margin v1
account: <lowercase 0x address>
enabled: <true|false>
nonce: <ms since epoch>
```

The cancel scope is signed, so a signature for `symbols: ["META"]` cannot be replayed as a whole-account cancel. Symbols are uppercased and sorted before joining, so the same request in a different order is the same envelope — you do not have to sort before signing. A symbol containing a comma or a newline is refused rather than encoded.

## Unsigned orders

`/v1/order` accepts a plain `x-paper-token` with no signature **only while signing is optional**. With `STRIKE_REQUIRE_SIGNED_ORDERS=1` — and always in `STRIKE_FUNDING_MODE=deposits`, which implies it regardless of that flag — every mutating trade route returns `401` without a valid session signature: `/v1/order`, `/v1/cancel`, `/v1/margin`, and the legacy `/v1/quotes` ladder push. Deposits mode does not consult the signing flag at all, because real money must not ride on two flags agreeing.

Sign your orders.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.strikelabs.app/api-reference/session-keys.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
