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

# API

> Using the API directly to configure Chipotle and execute actions.

## Using the API directly

The same workflows can be done via the REST API. The API itself is under `/core/v1/`. All endpoints *that require authentication* expect the API key in a header:

* `X-Api-Key: your-api-key`
* or `Authorization: Bearer your-api-key`

Examples below assume `KEY=your_account_or_usage_api_key` and `BASE=https://api.chipotle.litprotocol.com` (the hosted production API). The JavaScript examples use the Core SDK (`LitNodeSimpleApiClient`) from `core_sdk.js`.

**API workflow:**

1. [New account or verify account (login)](#1-new-account-or-verify-account-login)
2. [Add funds](#2-add-funds)
3. [Add usage API key](#3-add-usage-api-key)
4. [Create wallet (PKP)](#4-create-a-wallet-pkp)
5. [Add group and register IPFS action](#5-add-group-and-register-ipfs-action)
6. [Add PKP to group (optional)](#6-add-pkp-to-group-optional)
7. [Run lit-action](#7-run-lit-action)

### 1. New account or verify account (login)

Create a new account (returns API key and wallet address). Or verify an existing key with the `account_exists` function.

<Tabs>
  <Tab title="JavaScript (Core SDK)">
    ```javascript theme={null}
    import { createClient } from './core_sdk.js';

    const client = createClient('https://api.chipotle.litprotocol.com');

    // New account
    const res = await client.newAccount({
      accountName: 'My App',
      accountDescription: 'Optional description',
      email: 'optional@example.com'  // optional — forwarded to Stripe
    });
    console.log('API key:', res.api_key);
    console.log('Wallet:', res.wallet_address);
    // Store res.api_key securely.

    // Or verify existing key (login)
    const exists = await client.accountExists(res.api_key);
    console.log('Account exists:', exists);
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    # New account
    curl -s -X POST "https://api.chipotle.litprotocol.com/core/v1/new_account" \
      -H "Content-Type: application/json" \
      -d '{"account_name":"My App","account_description":"Optional","email":"optional@example.com"}'

    # Verify account (replace KEY with your API key)
    curl -s "https://api.chipotle.litprotocol.com/core/v1/account_exists" \
      -H "X-Api-Key: KEY"
    ```
  </Tab>
</Tabs>

<Note>
  The very first call takes \~15 seconds — account creation waits for an on-chain
  transaction on Base. Every other call below returns in well under a second.
</Note>

### 2. Add funds

Lit Action execution and write/metered management calls (including the steps
below) consume credits; read-only calls are free. Add funds with a credit card
in the [Dashboard](/management/pricing#paying-with-a-credit-card-via-stripe),
with [crypto](/management/crypto) (ETH, USDC, SOL and more) or
[LITKEY](/management/litkey), or programmatically via the
[billing API](#billing). Without credits the steps below return
`402 Payment Required` — see [Errors](/management/errors).

### 3. Add usage API key

Create a usage API key with fine-grained permissions. The response includes the new key only once — store it immediately.

<Tabs>
  <Tab title="JavaScript (Core SDK)">
    ```javascript theme={null}
    const res = await client.addUsageApiKey({
      apiKey: accountApiKey,
      name: 'My Usage Key',
      description: 'Used by my dApp',
      canCreateGroups: false,
      canDeleteGroups: false,
      canCreatePkps: false,
      manageIpfsIdsInGroups: [],   // group IDs; 0 = wildcard (all groups)
      addPkpToGroups: [],
      removePkpFromGroups: [],
      executeInGroups: [groupId]   // grant execute permission for specific groups
    });
    console.log('New usage API key (store it now):', res.usage_api_key);
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -s -X POST "https://api.chipotle.litprotocol.com/core/v1/add_usage_api_key" \
      -H "Content-Type: application/json" \
      -H "X-Api-Key: KEY" \
      -d '{
        "name": "My Usage Key",
        "description": "Used by my dApp",
        "can_create_groups": false,
        "can_delete_groups": false,
        "can_create_pkps": false,
        "manage_ipfs_ids_in_groups": [],
        "add_pkp_to_groups": [],
        "remove_pkp_from_groups": [],
        "execute_in_groups": [1]
      }'
    ```
  </Tab>
</Tabs>

**Permission fields:**

| Field                       | Type    | Description                                                                                   |
| --------------------------- | ------- | --------------------------------------------------------------------------------------------- |
| `can_create_groups`         | bool    | Allow this key to create new groups                                                           |
| `can_delete_groups`         | bool    | Allow this key to delete groups                                                               |
| `can_create_pkps`           | bool    | Allow this key to create PKPs                                                                 |
| `manage_ipfs_ids_in_groups` | `u64[]` | Group IDs where this key can add/remove IPFS actions. Use `[0]` as a wildcard for all groups. |
| `add_pkp_to_groups`         | `u64[]` | Group IDs where this key can add PKPs. Use `[0]` for all groups.                              |
| `remove_pkp_from_groups`    | `u64[]` | Group IDs where this key can remove PKPs. Use `[0]` for all groups.                           |
| `execute_in_groups`         | `u64[]` | Group IDs where this key can execute lit-actions. Use `[0]` for all groups.                   |

### 4. Create a wallet (PKP)

Request a new wallet (PKP) for the account. The server returns the wallet address and registers it. (Every account also starts with wallet id `0` — the Account Master Wallet created at signup.)

<Tabs>
  <Tab title="JavaScript (Core SDK)">
    ```javascript theme={null}
    const res = await client.createWallet(accountApiKey);
    console.log('Wallet address:', res.wallet_address);
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -s -X POST "https://api.chipotle.litprotocol.com/core/v1/create_wallet" \
      -H "X-Api-Key: KEY"
    ```
  </Tab>
</Tabs>

<Note>
  `GET /create_wallet` also works but is deprecated — minting is a metered write,
  and GETs get replayed by prefetchers and retrying proxies. Use POST.
</Note>

### 5. Add group and register IPFS action

Create a group, then add an action (IPFS CID) to scope which keys can run it.

<Tabs>
  <Tab title="JavaScript (Core SDK)">
    ```javascript theme={null}
    // Create group
    await client.addGroup({
      apiKey: accountApiKey,
      groupName: 'My Group',
      groupDescription: 'Optional',
      pkpIdsPermitted: [],        // PKP IDs pre-permitted in this group
      cidHashesPermitted: []      // CID hashes pre-permitted in this group
    });

    // List groups to get the new group ID
    const groups = await client.listGroups({ apiKey: accountApiKey, pageNumber: '0', pageSize: '10' });
    const groupId = groups[groups.length - 1].id;

    // Add an IPFS action (CID) to the group
    await client.addActionToGroup({
      apiKey: accountApiKey,
      groupId,                              // u64
      actionIpfsCid: 'QmYourIpfsCidHere'   // CID is hashed on the server
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    # Create group
    curl -s -X POST "https://api.chipotle.litprotocol.com/core/v1/add_group" \
      -H "Content-Type: application/json" \
      -H "X-Api-Key: KEY" \
      -d '{
        "group_name": "My Group",
        "group_description": "",
        "pkp_ids_permitted": [],
        "cid_hashes_permitted": []
      }'
    # Response: {"success":true,"group_id":"1"}

    # Add action to group (use group_id from the response above)
    curl -s -X POST "https://api.chipotle.litprotocol.com/core/v1/add_action_to_group" \
      -H "Content-Type: application/json" \
      -H "X-Api-Key: KEY" \
      -d '{"group_id": 1, "action_ipfs_cid": "QmYourIpfsCidHere"}'
    ```
  </Tab>
</Tabs>

### 6. Add PKP to group (optional)

Restrict which wallets (PKPs) can be used in the group by adding their IDs to the group.

<Tabs>
  <Tab title="JavaScript (Core SDK)">
    ```javascript theme={null}
    await client.addPkpToGroup({
      apiKey: accountApiKey,
      groupId,         // u64
      pkpId: walletId  // PKP ID from listWallets or createWallet
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -s -X POST "https://api.chipotle.litprotocol.com/core/v1/add_pkp_to_group" \
      -H "Content-Type: application/json" \
      -H "X-Api-Key: KEY" \
      -d '{"group_id": 1, "pkp_id": "YOUR_PKP_ID"}'
    ```
  </Tab>
</Tabs>

### 7. Run lit-action

Execute a lit-action by sending JavaScript code and optional params. Use a usage API key (or account key) in the header.

<Tabs>
  <Tab title="JavaScript (Core SDK)">
    ```javascript theme={null}
    const result = await client.litAction({
      apiKey: usageApiKey,
      code: `
        async function main({ pkpId }) {
          const wallet = new ethers.Wallet(await Lit.Actions.getPrivateKey({ pkpId }));
          const sig = await wallet.signMessage("Hello from Lit Action");
          return { sig };
        }
      `,
      jsParams: { pkpId: '0x...' }
    });
    console.log(result.response, result.logs);
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -s -X POST "https://api.chipotle.litprotocol.com/core/v1/lit_action" \
      -H "Content-Type: application/json" \
      -H "X-Api-Key: KEY" \
      -d '{"code":"async function main() { return \"hello\"; }","js_params":null}'
    ```
  </Tab>
</Tabs>

<Warning>
  **Newly-granted permissions are eventually consistent — verify before you depend on them.** After `add_usage_api_key` (or `add_action_to_group` / `add_pkp_to_group`) there's a short, variable delay before the grant works on `/lit_action`, so the first call right after minting a key can fail. Don't sleep a fixed amount (it flakes or over-waits) — poll the real path (run the actual action with the actual key) until it succeeds, then proceed. A small retry on network errors and `5xx` helps too. Worked version: `waitForUsageKeyReady` + retry wrapper in [`examples/action-bound-wallet`](https://github.com/LIT-Protocol/chipotle/tree/main/examples/action-bound-wallet).
</Warning>

### Other useful endpoints

<Info>
  **Raw CID vs hashed CID:** Some endpoints accept a raw IPFS CID (`action_ipfs_cid`, e.g. `"QmYour..."`) — the server hashes it for you. Other endpoints require the already-hashed CID (`hashed_cid`, e.g. `"0xabc..."`) — a keccak256 hex string you get back from `list_actions`. As a rule: **creation endpoints** (`add_action`, `add_action_to_group`) take the raw CID; **update/delete endpoints** (`delete_action`, `remove_action_from_group`, `update_action_metadata`) take the hashed CID.
</Info>

**Read / list (no billing charge):**

* **`GET /list_api_keys?page_number&page_size`** — List usage API keys (paginated). Returns metadata only — key values are not returned.
* **`GET /list_groups?page_number&page_size`** — List groups.
* **`GET /list_wallets?page_number&page_size`** — List wallets (PKPs).
* **`GET /list_wallets_in_group?group_id&page_number&page_size`** — List wallets in a group (`group_id` is a u64).
* **`GET /list_actions?[group_id]&page_number&page_size`** — List actions. When `group_id` is provided, lists actions in that group. When omitted, lists all actions on the account.
* **`GET /get_node_chain_config`** — Returns chain config, including contract addresses. No auth required.
* **`GET /get_api_payers`** — Returns the list of API payer addresses. No auth required.
* **`GET /get_admin_api_payer`** — Returns the admin payer address. No auth required.
* **`POST /get_lit_action_ipfs_id`** — Compute the IPFS CID for a given JS code string. Body is a JSON string. No auth required.

**Mutating management (billed):**

* **`POST /remove_usage_api_key`** — Delete a usage key. Body: `{"usage_api_key": "..."}`.
* **`POST /update_usage_api_key`** — Update all permissions on an existing usage key. Body: same shape as `add_usage_api_key`, plus `usage_api_key`.
* **`POST /update_usage_api_key_metadata`** — Update only the name/description of a usage key. Body: `{"usage_api_key": "...", "name": "...", "description": "..."}`.
* **`POST /remove_group`** — Delete a group. Body: `{"group_id": "..."}`.
* **`POST /update_group`** — Update group name, description, and permitted PKP IDs / CID hashes. Body: `{"group_id": 1, "name": "...", "description": "...", "pkp_ids_permitted": [], "cid_hashes_permitted": []}`.
* **`POST /add_action`** — Register a standalone action (name + description + IPFS CID). Body: `{"action_ipfs_cid": "Qm...", "name": "...", "description": "..."}`.
* **`POST /delete_action`** — Delete an action and its metadata from the account. Body: `{"hashed_cid": "0x..."}` (already-hashed CID).
* **`POST /remove_action_from_group`** — Remove an IPFS action from a group. Body: `{"group_id": 1, "hashed_cid": "0x..."}` (already-hashed CID).
* **`POST /update_action_metadata`** — Update the name/description of a registered action. Body: `{"hashed_cid": "0x...", "name": "...", "description": "..."}`.
* **`POST /remove_pkp_from_group`** — Remove a PKP from a group. Body: `{"group_id": 1, "pkp_id": "..."}`.

#### Billing

* **`GET /billing/stripe_config`** — Returns the Stripe publishable key. No auth required.
* **`GET /billing/balance`** — Returns the current credit balance for the authenticated account.
* **`POST /billing/create_payment_intent`** — Creates a Stripe PaymentIntent. Body: `{"amount_cents": 500}` (minimum 500 = \$5.00). Returns `client_secret` for use with Stripe.js.
* **`POST /billing/confirm_payment`** — Verifies a succeeded PaymentIntent and credits the account. Body: `{"payment_intent_id": "pi_..."}`.

### ChainSecured-mode HTTP endpoints

Three HTTP endpoints back the ChainSecured (sovereign) flow. They sit alongside the on-chain writes — see [API mode vs ChainSecured mode](/management/account_modes) for the full SDK-side workflow and when to choose each mode.

All three (and the billing-auth header described above) share the same EIP-712 typed-data envelope. The wallet UI displays a labelled struct — `address` and `issuedAt` fields under a stable `(name: "Lit ChainSecured", version: "1", chainId)` domain — so users can see exactly what they're signing. The `primaryType` pins the signature to a specific flow and is part of the EIP-712 type hash, so a signature minted for one endpoint is rejected by every other endpoint at the digest level. Typed-data payloads longer than 4 KiB (serialised JSON) are rejected.

| Endpoint                            | `primaryType`    |
| ----------------------------------- | ---------------- |
| `/create_wallet_with_signature`     | `CreateWallet`   |
| `/convert_to_chain_secured_account` | `ConvertAccount` |
| `/add_usage_api_key_with_signature` | `AddUsageApiKey` |
| `X-Wallet-Auth` billing header      | `BillingAuth`    |

The canonical typed-data shape (must match exactly — field declaration order is part of the EIP-712 type hash):

```json theme={null}
{
  "types": {
    "EIP712Domain": [
      { "name": "name",    "type": "string"  },
      { "name": "version", "type": "string"  },
      { "name": "chainId", "type": "uint256" }
    ],
    "<primaryType>": [
      { "name": "address",  "type": "address" },
      { "name": "issuedAt", "type": "uint256" }
    ]
  },
  "primaryType": "<primaryType>",
  "domain": {
    "name": "Lit ChainSecured",
    "version": "1",
    "chainId": "8453"
  },
  "message": {
    "address":  "0x…",
    "issuedAt": "<unix-seconds>"
  }
}
```

The server enforces a ±5-minute window on `issuedAt` as the only replay protection — no nonce store. Worst-case replay on the unauthenticated mint endpoints just produces an extra unattached PKP (compute cost only — see each endpoint below for specifics).

#### `POST /create_wallet_with_signature`

Mints a PKP via DStack MPC after verifying a wallet-ownership signature. Used by `createWallet` in ChainSecured mode; the response is then passed to the on-chain `registerWalletDerivation` call (signed by the same wallet) to register the PKP to the account.

No API key required. `primaryType` must be `CreateWallet`.

**Request body:**

```json theme={null}
{
  "typed_data": { /* canonical EIP-712 typed data with primaryType: "CreateWallet" */ },
  "signature": "0x<65-byte hex>"
}
```

**Response:**

```json theme={null}
{
  "wallet_address": "0x...",
  "derivation_path": "0x..."
}
```

Pass `derivation_path` verbatim into `registerWalletDerivation(adminHash, wallet_address, derivation_path, name, description)` on the AccountConfig contract — until that lands, the PKP exists in MPC but is not registered to any account.

```bash theme={null}
curl -s -X POST "https://api.chipotle.litprotocol.com/core/v1/create_wallet_with_signature" \
  -H "Content-Type: application/json" \
  -d '{
    "typed_data": {
      "types": {
        "EIP712Domain": [
          {"name":"name","type":"string"},
          {"name":"version","type":"string"},
          {"name":"chainId","type":"uint256"}
        ],
        "CreateWallet": [
          {"name":"address","type":"address"},
          {"name":"issuedAt","type":"uint256"}
        ]
      },
      "primaryType": "CreateWallet",
      "domain": {"name":"Lit ChainSecured","version":"1","chainId":"8453"},
      "message": {"address":"0xabc...","issuedAt":"1745798400"}
    },
    "signature": "0x..."
  }'
```

#### `POST /convert_to_chain_secured_account`

Flips a managed (API-mode) account to ChainSecured (unmanaged) in a single on-chain transaction. The account's `apiKeyHash` is preserved — groups, PKPs, action metadata, and usage API keys (everything keyed by `apiKeyHash` on-chain) stay attached. Only the admin wallet and the `managed` flag change on-chain. **Billing should be re-verified after conversion:** Stripe credits are associated with the Stripe customer resolved from the current admin wallet address, so a credit balance is not guaranteed to carry over automatically when the admin wallet changes. There is no reverse path.

Requires the existing master API key (sent via `X-Api-Key` or `Authorization: Bearer`, per the auth header conventions above). The server's api\_payer signs the on-chain conversion after verifying the wallet signature; the new admin must sign EIP-712 typed data with `primaryType: "ConvertAccount"`.

**Request body:**

```json theme={null}
{
  "new_admin_wallet_address": "0x...",
  "typed_data": { /* primaryType: "ConvertAccount" */ },
  "signature": "0x..."
}
```

```bash theme={null}
curl -s -X POST "https://api.chipotle.litprotocol.com/core/v1/convert_to_chain_secured_account" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: KEY" \
  -d '{
    "new_admin_wallet_address":"0xabc...",
    "typed_data": {
      "types": {
        "EIP712Domain": [
          {"name":"name","type":"string"},
          {"name":"version","type":"string"},
          {"name":"chainId","type":"uint256"}
        ],
        "ConvertAccount": [
          {"name":"address","type":"address"},
          {"name":"issuedAt","type":"uint256"}
        ]
      },
      "primaryType": "ConvertAccount",
      "domain": {"name":"Lit ChainSecured","version":"1","chainId":"8453"},
      "message": {"address":"0xabc...","issuedAt":"1745798400"}
    },
    "signature":"0x..."
  }'
```

See [API mode vs ChainSecured mode → Converting an API account to ChainSecured](/management/account_modes#converting-an-api-account-to-chainsecured) for the dashboard flow, the SDK wrapper (`client.convertToChainSecuredAccount`), and the post-conversion verification checklist.

#### `POST /add_usage_api_key_with_signature`

The ChainSecured counterpart to `/add_usage_api_key`. Mirrors `create_wallet_with_signature`: the server mints a usage-key wallet via DStack MPC after verifying a wallet-ownership signature, then returns the secret (base64-encoded) plus the wallet address and derivation path. The client follows up on-chain — only the admin wallet of a ChainSecured account can call `setUsageApiKey`, so the server cannot complete the attach for you.

No API key required. `primaryType` must be `AddUsageApiKey`. Worst-case replay just returns a fresh secret for an unattached wallet — equivalent to a freshly generated keypair until the admin wallet calls the on-chain follow-ups, so compute cost only.

**Request body:**

```json theme={null}
{
  "typed_data": { /* primaryType: "AddUsageApiKey" */ },
  "signature": "0x<65-byte hex>"
}
```

**Response:**

```json theme={null}
{
  "usage_api_key": "<base64 of 32 secret bytes>",
  "wallet_address": "0x...",
  "derivation_path": "0x..."
}
```

The client must do two on-chain calls signed by the admin wallet to attach the new usage key:

1. `registerWalletDerivation(adminHash, wallet_address, derivation_path, name, description)` — registers the PKP to the account.
2. `setUsageApiKey(adminHash, keccak256(usage_api_key_bytes), expiration, balance, name, description, …permissions)` — attaches the usage key with its permission set.

Until both land, the secret in `usage_api_key` is just a freshly minted keypair with no on-chain identity.

```bash theme={null}
curl -s -X POST "https://api.chipotle.litprotocol.com/core/v1/add_usage_api_key_with_signature" \
  -H "Content-Type: application/json" \
  -d '{
    "typed_data": {
      "types": {
        "EIP712Domain": [
          {"name":"name","type":"string"},
          {"name":"version","type":"string"},
          {"name":"chainId","type":"uint256"}
        ],
        "AddUsageApiKey": [
          {"name":"address","type":"address"},
          {"name":"issuedAt","type":"uint256"}
        ]
      },
      "primaryType": "AddUsageApiKey",
      "domain": {"name":"Lit ChainSecured","version":"1","chainId":"8453"},
      "message": {"address":"0xabc...","issuedAt":"1745798400"}
    },
    "signature":"0x..."
  }'
```

***

Both request/response shapes and OpenAPI spec are available directly in the dev system. For a Swagger UI implementation of the OpenAPI spec, please browse to:\
\
[https://api.chipotle.litprotocol.com/core/v1/swagger-ui](https://api.chipotle.litprotocol.com/core/v1/swagger-ui)

### Open API Specification

The OpenAPI spec itself can be found at:\
\
[https://api.chipotle.litprotocol.com/core/v1/openapi.json](https://api.chipotle.litprotocol.com/core/v1/openapi.json)

Note that these specs are subject to minor changes and will always be available with the dev server endpoints.
