> ## Documentation Index
> Fetch the complete documentation index at: https://docs.circuit.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Kraken

> Place, view, and cancel spot orders and read balances on the Kraken exchange.

Use the Kraken integration to build agents that trade **spot** markets on the Kraken centralized exchange. Unlike the wallet-backed venues, Kraken is authorized by an **exchange credential** you connect once - your agent never sees the API key or secret, only an opaque `credentialRef`.

The surface splits into three tiers by what each call needs:

| Tier              | Methods                                 | Needs                                                                                              |
| ----------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------- |
| **Money writes**  | `placeOrder`, `cancelOrder`             | a credential with the **trade** permission (gated by the policy firewall + recorded in the ledger) |
| **Account reads** | `balances`, `openOrders`, `orderStatus` | a credential with the **read** permission (signed, not ledgered)                                   |
| **Market data**   | `orderBook`, `ticker`                   | **nothing** - public, unauthenticated                                                              |

<Note>
  The MVP supports **fully collateralized spot trading only**. Margin, leverage, and any non-spot order fields are rejected. Withdrawals are out of scope - connect trade-only keys with no withdrawal permission.
</Note>

### Declare the credential requirement

Agents declare the Kraken credential they need in `circuit.toml`. Circuit turns this into a start-time credential picker for the user and seats a runtime permit scoped to the selected credential.

```toml theme={null}
[exchangeCredentials.kraken]
read = true
trade = true
minimumAllocationUsd = 25
```

Read-only agents can set `trade = false`. Trading requires `read = true` because Circuit checks live balances before submitting an order. Trading agents also declare `minimumAllocationUsd`; users choose a Kraken virtual funding allocation at or above that amount when starting the session.

Users connect credentials from Settings in the web app or from the CLI:

```bash theme={null}
circuit kraken connect
circuit kraken list
```

### The credential reference

Every credentialed call carries a `credentialRef`, never a key or secret. The credential the user selected when starting the session is exposed as a ready-made ref on `agent.credentials`:

<CodeGroup>
  ```python Python theme={null}
  credential_ref = agent.credentials.kraken
  ```

  ```typescript TypeScript theme={null}
  const credentialRef = agent.credentials.kraken!;
  ```
</CodeGroup>

For agents that declare `trade = true`, `agent.allocation` contains invocation-start available session cash as `kraken:allocation:USD` and each positive base-asset inventory the session may sell. Available cash subtracts filled net deployment and accepted orders' unfilled limit notional from the original slice. Base entries carry the exact decimal `krakenMetadata.availableBaseVolume` enforced by the sell gate:

<CodeGroup>
  ```python Python theme={null}
  allocation = agent.allocation
  kraken_available_cash_usd = next(
      (float(b.market_value_usd) for b in allocation.balances if b.asset_key == "kraken:allocation:USD"),
      0.0,
  )
  eth_usdc_available = next(
      (
          b.kraken_metadata.available_base_volume
          for b in allocation.balances
          if b.kraken_metadata and b.token_address.upper() == "ETH"
      ),
      "0",
  )
  ```

  ```typescript TypeScript theme={null}
  const allocation = agent.allocation;
  const krakenAvailableCashUsd = Number(
    allocation.balances.find(b => b.assetKey === "kraken:allocation:USD")?.marketValueUsd ?? "0",
  );
  const ethUsdcAvailable = allocation.balances.find(
    b => b.krakenMetadata && b.tokenAddress.toUpperCase() === "ETH",
  )?.krakenMetadata?.availableBaseVolume ?? "0";
  ```
</CodeGroup>

The USD and base inventory entries are session-scoped projections of durable accepted orders and watcher-ingested Kraken fills at invocation start. Before every order Circuit recomputes both authorities, so an earlier order can make the gate stricter than the snapshot. Track dependent steps locally or wait for the next trigger's fresh context. A missing base entry means zero sellable inventory; never size a sell from the whole-account balance.

The initial USD slice is backed by the account's aggregate free USD-priced value, including holdings such as ETH and USDT. Circuit exposes that virtual allocation as synthetic `kraken:allocation:USD`; it is the session's virtual-allocation ceiling, not a claim that the account already holds the quote asset a particular order needs. Prompt the agent to inspect whole-account balances and choose `/USD` or `/USDC` appropriately. The live order gate still requires enough free balance in the concrete quote, and Circuit does not convert holdings automatically at start.

The API key and secret are sealed server-side; the SDK and your agent code only ever hold the reference. Circuit turns it into a signed Kraken request at the signing boundary - the same model as the server-resolved wallet address used by the other venues.

### Pairs and assets

Kraken pairs and assets are **venue-namespaced** with a `KRAKEN:` prefix. Both the slashed and concatenated spellings are accepted everywhere (`"KRAKEN:XBT/USD"` and `"KRAKEN:XBTUSD"` are the same pair); responses (open orders, order status) return the concatenated form (`"KRAKEN:XBTUSD"`) - compare pairs case-insensitively after stripping the `/` rather than by raw string equality. Balances come back as venue-namespaced assets (`"KRAKEN:XXBT"`, `"KRAKEN:ZUSD"`). All amounts are **strings** to preserve precision.

**Order limits.** Trading is enabled for a fixed pair set: `XBT`, `ETH`, `SOL` against `USD` or `USDC`. Sell minimums are `0.0001` XBT, `0.01` ETH, and `0.1` SOL. Buy minimums are `0.000102` XBT, `0.0102` ETH, and `0.102` SOL: the extra 2% keeps a fully filled minimum buy sellable after its base-denominated Kraken fee. A positive partial-fill residue below the sell minimum remains accounted to the session but is venue dust, so it is not exposed as spendable inventory. A per-order maximum notional cap applies (volume × price in USD). Orders outside these bounds are rejected before signing.

### Place Order

Place a spot order. In **auto mode** the order is claimed, signed, and submitted immediately; in **manual mode** it is captured as a suggestion for the user to approve (`data` is then a suggestion envelope `{ suggested: true, suggestionId }` - see [Manual vs Auto Mode](../concepts/manual-vs-auto-mode)).

<CodeGroup>
  ```python Python theme={null}
  def place_order(request: KrakenPlaceOrderRequest | KrakenPlaceOrderRequestInput) -> KrakenPlaceOrderResponse
  ```

  ```typescript TypeScript theme={null}
  async placeOrder(request: KrakenPlaceOrderRequest): Promise<KrakenPlaceOrderResponse>
  ```
</CodeGroup>

**Request Parameters:**

* `credentialRef` (object): `{ kind: "exchange", id: string }` - the connected exchange credential.
* `order` (object):
  * `pair` (string): Venue-namespaced pair, e.g. `"KRAKEN:XBT/USD"`.
  * `side` (string): `"buy"` or `"sell"`.
  * `type` (string): `"limit"`. Market orders are **rejected**. A limit price bounds execution; for buys it also gives virtual-allocation enforcement a maximum `volume × price` reservation. Use a marketable limit (a price at or through the touch) for immediate fills.
  * `volume` (string): Order volume in base-asset units.
  * `price` (string): Limit price. Required.
* `validate` (boolean, optional): When `true`, Kraken validates the order **without** placing it (dry-run / preflight). No order id is returned.
* `idempotencyKey` (string, optional): Durable retry key for auto-mode orders. SDKs generate one automatically; provide a stable key only when retrying the same logical order after a process restart. Circuit derives Kraken's `cl_ord_id` from it, so an ambiguous submission is recovered by status lookup rather than resubmitted.
* `expiresAt` (string | null, optional): ISO 8601 timestamp. In manual mode, the suggestion expires at this time.

**Response:**

* `success` (boolean)
* `data` (object): `orderIds` (string\[]), `clientOrderId` (string | null), `description` (string | null). For a `validate` dry-run, `orderIds` is empty.
* `error` (string | null)

**Example:**

<CodeGroup>
  ```python Python theme={null}
  result = agent.platforms.kraken.place_order({
      "credentialRef": credential_ref,
      "order": {
          "pair": "KRAKEN:XBT/USD",
          "side": "buy",
          "type": "limit",
          "volume": "0.01",
          "price": "50000",
      },
  })
  if result.success and result.data:
      agent.log(f"Kraken order: {result.data.order_ids}")
  ```

  ```typescript TypeScript theme={null}
  const result = await agent.platforms.kraken.placeOrder({
    credentialRef,
    order: { pair: "KRAKEN:XBT/USD", side: "buy", type: "limit", volume: "0.01", price: "50000" },
  });
  if (result.success && result.data) {
    await agent.log(`Kraken order: ${result.data.orderIds.join(", ")}`);
  }
  ```
</CodeGroup>

Before submitting, Circuit re-reads live Kraken facts and fails loud if it cannot prove both venue funding and session ownership. Orders may use either enabled quote (`USD` or `USDC`), but each buy must be funded by the chosen quote's live free balance. A buy reserves exactly `volume × limit price`; Circuit instructs Kraken to take buy fees from acquired base, so fees cannot overdraw the quote allocation. Sells take fees from quote proceeds and may use only base inventory acquired by this session's fills, minus filled sells and open sell reservations. Orders from another session never establish ownership.

### Cancel Order

Cancel an order by its Kraken order id. Manual mode captures the cancel as a suggestion, same as `placeOrder`.

<CodeGroup>
  ```python Python theme={null}
  def cancel_order(request: KrakenCancelOrderRequest | KrakenCancelOrderRequestInput) -> KrakenCancelOrderResponse
  ```

  ```typescript TypeScript theme={null}
  async cancelOrder(request: KrakenCancelOrderRequest): Promise<KrakenCancelOrderResponse>
  ```
</CodeGroup>

* `credentialRef` (object): The exchange credential.
* `orderId` (string): The Kraken order id (`txid`) to cancel.
* `idempotencyKey` (string, optional): Auto-generated when omitted.

**Response:** `data` is `{ count: number }` - the number of orders canceled.

<CodeGroup>
  ```python Python theme={null}
  result = agent.platforms.kraken.cancel_order({
      "credentialRef": credential_ref,
      "orderId": "OABCDEF-GHIJK-LMNOPQ",
  })
  ```

  ```typescript TypeScript theme={null}
  const result = await agent.platforms.kraken.cancelOrder({
    credentialRef,
    orderId: "OABCDEF-GHIJK-LMNOPQ",
  });
  ```
</CodeGroup>

### Read Balances

Read the credential's **whole-account** Kraken balances. Each entry carries both the total `balance` and the `available` amount (total minus funds on hold in open orders). Use this read to choose a funded quote asset and confirm venue availability; never use it as the session's ownership or spending limit. The `kraken:allocation:USD` allocation entry is the session's deployable virtual cash, and Circuit's order gate is the final authority on cash and sellable inventory.

<CodeGroup>
  ```python Python theme={null}
  def balances(request: KrakenBalancesRequest | KrakenBalancesRequestInput | None = None, *, credential_ref: dict | None = None) -> KrakenBalancesResponse
  ```

  ```typescript TypeScript theme={null}
  async balances(request: KrakenBalancesRequest): Promise<KrakenBalancesResponse>
  ```
</CodeGroup>

**Response:** `data` is an array of `{ asset, balance, available, hold }` (all strings; `asset` is venue-namespaced).

<CodeGroup>
  ```python Python theme={null}
  balances = agent.platforms.kraken.balances(credential_ref=credential_ref)
  if balances.success and balances.data:
      for b in balances.data:
          agent.log(f"{b.asset}: {b.available} available")
  ```

  ```typescript TypeScript theme={null}
  const balances = await agent.platforms.kraken.balances({ credentialRef });
  if (balances.success && balances.data) {
    for (const b of balances.data) await agent.log(`${b.asset}: ${b.available} available`);
  }
  ```
</CodeGroup>

### Open Orders / Order Status

`openOrders` lists the credential's currently open orders (optionally filtered by `pair`); `orderStatus` reads a single order by id. Because `openOrders` is whole-account state, retain the ids returned by this session's `placeOrder` calls and cancel only those ids - never cancel every order on a pair shared with another session or a human.

<CodeGroup>
  ```python Python theme={null}
  def open_orders(request: KrakenOpenOrdersRequest | ... | None = None, *, credential_ref=None, pair=None) -> KrakenOpenOrdersResponse
  def order_status(request: KrakenOrderStatusRequest | ... | None = None, *, credential_ref=None, order_id=None) -> KrakenOrderStatusResponse
  ```

  ```typescript TypeScript theme={null}
  async openOrders(request: KrakenOpenOrdersRequest): Promise<KrakenOpenOrdersResponse>
  async orderStatus(request: KrakenOrderStatusRequest): Promise<KrakenOrderStatusResponse>
  ```
</CodeGroup>

Each order carries: `orderId`, `clientOrderId` (string | null), `pair` (venue-namespaced), `side`, `type`, `price` (string | null), `volume`, `filled`, `status`, `openedAt` / `closedAt` (ISO 8601 | null).

<CodeGroup>
  ```python Python theme={null}
  open_orders = agent.platforms.kraken.open_orders(credential_ref=credential_ref)
  status = agent.platforms.kraken.order_status(credential_ref=credential_ref, order_id="OABCDEF-GHIJK-LMNOPQ")
  ```

  ```typescript TypeScript theme={null}
  const openOrders = await agent.platforms.kraken.openOrders({ credentialRef });
  const status = await agent.platforms.kraken.orderStatus({ credentialRef, orderId: "OABCDEF-GHIJK-LMNOPQ" });
  ```
</CodeGroup>

### Order Book / Ticker (public)

Public market data - no credential required. These are strategy inputs, not execution reads. Circuit retries rate-limited public reads with bounded backoff, so a transient `429` doesn't fail your loop.

<CodeGroup>
  ```python Python theme={null}
  def order_book(request: KrakenOrderBookQuery | ... | None = None, *, pair=None, depth=None) -> KrakenOrderBookResponse
  def ticker(pair: str) -> KrakenTickerResponse
  ```

  ```typescript TypeScript theme={null}
  async orderBook(query: KrakenOrderBookQuery): Promise<KrakenOrderBookResponse>
  async ticker(query: KrakenTickerQuery): Promise<KrakenTickerResponse>
  ```
</CodeGroup>

* `orderBook` - `data` is `{ pair, asks, bids }`, each level `{ price, volume, timestamp }`. `depth` (string, optional) caps the number of levels.
* `ticker` - `data` is `{ pair, ask, bid, last, volume }` (all strings).

<CodeGroup>
  ```python Python theme={null}
  book = agent.platforms.kraken.order_book(pair="KRAKEN:XBT/USD", depth="10")
  tick = agent.platforms.kraken.ticker("KRAKEN:XBT/USD")
  if tick.success and tick.data:
      agent.log(f"XBT/USD ask: {tick.data.ask}")
  ```

  ```typescript TypeScript theme={null}
  const book = await agent.platforms.kraken.orderBook({ pair: "KRAKEN:XBT/USD", depth: "10" });
  const tick = await agent.platforms.kraken.ticker({ pair: "KRAKEN:XBT/USD" });
  if (tick.success && tick.data) await agent.log(`XBT/USD ask: ${tick.data.ask}`);
  ```
</CodeGroup>

### Common Errors

Errors are returned in the `error` field of the response.

| Error                                                             | Cause                                                    | Solution                                               |
| ----------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------ |
| "Exchange credential not found"                                   | `credentialRef.id` doesn't resolve for this user         | Connect the credential and use its id                  |
| "Exchange credential is revoked" / "does not allow reads/trading" | The credential is revoked or lacks the needed permission | Reconnect with the required permission (read / trade)  |
| "Insufficient Kraken {asset} balance: have X, need Y"             | Live pre-trade balance check failed                      | Reduce order size or fund the account                  |
| "Kraken pair must be namespaced: {pair}"                          | Pair missing the `KRAKEN:` prefix                        | Use `"KRAKEN:XBT/USD"`                                 |
| "Kraken writes are disabled"                                      | The venue-wide kill switch is on                         | Transient operational halt; retry later                |
| "Kraken API error: EOrder:..."                                    | Kraken rejected the order (size, price, funds)           | Check order parameters against Kraken's tick/lot rules |
| "Kraken pair {pair} is not enabled for Kraken trading"            | Pair outside the enabled set                             | Trade XBT/ETH/SOL against USD/USDC                     |
| "Kraken market orders are disabled; use a limit order"            | `type: "market"` submitted                               | Send a marketable limit order instead                  |
| "Kraken order volume X is below the Y {pair} minimum"             | Below the side-specific pair minimum                     | Size at or above the buy or sell minimum above         |
| "Kraken order would exceed this agent's \$N virtual allocation"   | Session's metered notional exhausted                     | Lower the order size or the session's open exposure    |

### Notes

* All balance, price, and volume amounts are **strings** to preserve precision.
* Money writes are **exactly-once** by `idempotencyKey`: an ambiguous submission is recovered by looking the order up via its client order id, never blindly resubmitted.
* Kraken account balances are **not** merged into the virtual allocation - read them live via `balances` when a decision needs them.
* The API key and secret never reach the SDK, your agent, or the stateless engine - only the sealed credential and the signing boundary do.
* Credentials are connected via `circuit kraken connect` or the web app; **deleting** a credential is done from the web app's Settings → Kraken screen (blocked while any live session still uses it). There is no CLI disconnect command.

### See Also

* [Manual vs Auto Mode](../concepts/manual-vs-auto-mode) - How order/cancel suggestions are captured and approved
* [Suggestions](./suggestions) - Control suggestion expiry for manual-mode suggestions
* [Error Handling](./error-handling) - Response envelope and error patterns
