Skip to main content

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.

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. cURL snippets use BASE=https://api.chipotle.litprotocol.com (hosted production API) and JavaScript snippets use BASE=https://api.dev.litprotocol.com (hosted dev API); change the BASE value if you want to target the other environment. The JavaScript examples use the Core SDK (LitNodeSimpleApiClient) from core_sdk.js. API workflow:
  1. New account or verify account (login)
  2. Add funds (or via the billing API)
  3. Add usage API key
  4. Create wallet (PKP)
  5. Add group and register IPFS action
  6. Add PKP to group (optional)
  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.
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);

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.
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);
Permission fields:
FieldTypeDescription
can_create_groupsboolAllow this key to create new groups
can_delete_groupsboolAllow this key to delete groups
can_create_pkpsboolAllow this key to create PKPs
manage_ipfs_ids_in_groupsu64[]Group IDs where this key can add/remove IPFS actions. Use [0] as a wildcard for all groups.
add_pkp_to_groupsu64[]Group IDs where this key can add PKPs. Use [0] for all groups.
remove_pkp_from_groupsu64[]Group IDs where this key can remove PKPs. Use [0] for all groups.
execute_in_groupsu64[]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.
const res = await client.createWallet(accountApiKey);
console.log('Wallet address:', res.wallet_address);

5. Add group and register IPFS action

Create a group, then add an action (IPFS CID) to scope which keys can run it.
// 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
});

6. Add PKP to group (optional)

Restrict which wallets (PKPs) can be used in the group by adding their IDs to the group.
await client.addPkpToGroup({
  apiKey: accountApiKey,
  groupId,         // u64
  pkpId: walletId  // PKP ID from listWallets or createWallet
});

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.
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);

Other useful endpoints

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.
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 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.
EndpointprimaryType
/create_wallet_with_signatureCreateWallet
/convert_to_chain_secured_accountConvertAccount
/add_usage_api_key_with_signatureAddUsageApiKey
X-Wallet-Auth billing headerBillingAuth
The canonical typed-data shape (must match exactly — field declaration order is part of the EIP-712 type hash):
{
  "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:
{
  "typed_data": { /* canonical EIP-712 typed data with primaryType: "CreateWallet" */ },
  "signature": "0x<65-byte hex>"
}
Response:
{
  "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.
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:
{
  "new_admin_wallet_address": "0x...",
  "typed_data": { /* primaryType: "ConvertAccount" */ },
  "signature": "0x..."
}
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 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:
{
  "typed_data": { /* primaryType: "AddUsageApiKey" */ },
  "signature": "0x<65-byte hex>"
}
Response:
{
  "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.
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

Open API Specification

The OpenAPI spec itself can be found at:

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.