Join our community of builders on Discord!

TypeScript SDK

@lightchainai/sdk (source) runs an API key against the right network and, given a wallet, pays for calls by itself: it tops up the prepaid balance on a 402 (the default mode), or signs each call's own payment (per-call mode, experimental). Completions go through the OpenAI SDK.
CodeBASH
An ES module for Node 18 or later; it uses only the platform's fetch, so it also runs in browsers. Wallets are viem local accounts.
Versions: this page describes the SDK's main branch. Per-call mode ships in the release after 0.1.0-alpha.0. In 0.1.0-alpha.0 it is called payment: "x402", and a call with no key needs keyless: true.

One bot, one API key

CodeTYPESCRIPT
This is the default pattern. The bot holds the key and nothing else: no wallet, no funds. The key's lifetime spend cap is the bot's budget. The funds stay in your prepaid balance, and you top it up in the chat. A leaked key is revoked and replaced, with no funds to move. Each completion is one job on chain, paid from the prepaid balance of the wallet that created the key. Its lightchain field (type LightchainJob) names the job: job_id, session_id, tx_hash, worker, and dropped_messages when the job left old messages out. See Verifying an Answer. Until the wallet behind the key has paid, and whenever its balance or allowance runs out, a call answers 402 (delegate_not_authorized, insufficient_balance, allowance_exhausted). With no account, lc.fetch hands it back as the API answered it, and the OpenAI SDK raises it as an APIError: a human tops up. Past the key's cap, the call answers 402 spend_cap_exceeded.

Automatic top-ups

To run a bot unattended, give it the wallet behind the key as account. On a 402 for an empty balance or allowance, lc.fetch then sends the depositAndAuthorize the 402 names from that wallet, and sends the request again once. This is the default mode (payment: "delegate"); per-call mode never deposits by itself.
CodeTYPESCRIPT
  • How much: the deposit is depositWei, or the 402's minimum (one job's fee) when depositWei is not set. A 402 asking for more than depositWei is not paid.
  • What is checked first: the chain id, the wallet (the key must be this wallet's) and the contract (this network's JobRegistry). A 402 that fails a check is not paid: it comes back as a 402 with the reason at the start of error.message. A spend_cap_exceeded 402 comes back as it is.
  • Trust: the delegate and the fee come from the API; the SDK trusts the API it talks to for those, as it trusts it with your key. depositWei bounds what one 402 can make it send.
  • Concurrency: each 402 pays its own deposit, one transaction after the other. Calls made together before the first deposit lands each deposit; what one of them did not need stays in the prepaid balance, and withdrawBalance on JobRegistry takes it back.

Pay per call (experimental)

Experimental: this mode, its options and the prepaid-debit scheme may change in any release. It uses the x402 v2 HTTP headers with LightChain's own payment scheme, so standard x402 clients can't pay it.
With payment: "per-call", the SDK pays each call from the account's own prepaid balance with a signed debit authorization. The account authorizes no delegate and sends no transaction per call. It needs account; apiKey is optional.
CodeTYPESCRIPT
On a 402 listing the prepaid-debit requirements, lc.fetch signs an EIP-712 debit authorization for one job's fee (at most maxPaymentWei), valid for maxTimeoutSeconds, with a random nonce, and sends the request again with it in PAYMENT-SIGNATURE. The API settles it before it answers: one JobRegistry transaction submits the job and debits the fee. lightchain.tx_hash is that settlement, and onPayment gets it with the fee debited.
  • No apiKey, no key at all. lc.fetch drops the Authorization header, and lc.apiKey is a placeholder for the OpenAI SDK, which insists on one. The account is held, as payer, to the per-payer limits: by default 30 requests a minute and one call in flight (see Limits).
  • With a key, lc.fetch sends it on every call, and the call is held to the key's limits. A key whose wallet has a delegate gets no 402, so the SDK pays only the calls the API asks it to pay.
  • The balance is yours to fund. The SDK never deposits in this mode. When the balance can't cover a call, it fails with 402 insufficient_funds: lc.deposit() and retry. lc.getBalance() tells you how much is left.
  • Checked before signing: network is eip155:<chainId>, asset is the network's JobRegistry, the domain is LightChain JobRegistry version 1, payTo and facilitatorAddress are addresses, maxTimeoutSeconds is between 1 and 600, and amount is at most maxPaymentWei. Requirements that fail are not signed.
  • Refused payments come back as the API answered them, with the reason as error.code, and are not paid again: see Payment.
  • Retries: an OpenAI SDK retry is a new call and pays again; set maxRetries: 0 to decide yourself.
  • The modes do not mix. Per-call mode never sends depositAndAuthorize, and the default mode never signs a per-call payment.

Options by mode

ModeapiKeyaccountMode-only options
default (payment: "delegate")requiredoptional: with it, fetch tops up on a 402depositWei, onDeposit
payment: "per-call" (experimental)optional: without it, no key is sentrequired: it signs each payment, and never depositsmaxPaymentWei (required), onPayment
The constructor throws on any other combination, and on an option the mode would ignore, so a cap is never silently dropped.

Members

MemberDoes
new Lightchain({ network, apiKey?, account?, payment?, ..., fetch?, transport? })network is "mainnet", "testnet", or your own Network. fetch replaces the HTTP client; transport replaces the chain's JSON-RPC transport.
fetchfetch that sends the API key to the network's API (and to no other host) and, given an account, pays a 402 the way payment says and sends the request again, once. Give it to the OpenAI SDK.
baseURL, apiKeyThe OpenAI SDK's baseURL (the network's /v1) and apiKey (a placeholder when there is no key).
getBalance(delegate?)The account's prepaid balance; with delegate, also whether it is authorized and its remaining allowance. Needs account.
deposit(value)Adds value to the prepaid balance and authorizes nobody: what per-call payments pay from. Needs account.
depositAndAuthorize(delegate, value)Adds value to the balance, authorizes delegate (the API's signer) and raises its allowance by value. Needs account.
address, networkThe account's address, if any, and the endpoints in use.
deposit and depositAndAuthorize resolve with the transaction hash once it succeeded on chain. Types: LightchainOptions, LightchainJob, Balance, Deposit (a top-up the SDK sent: hash, value, delegate), Payment (a per-call payment: hash, amount), Network.

Networks

networks holds the published endpoints and addresses; jobRegistryAbi holds the JobRegistry functions the SDK calls.
ChainAPI (apiUrl)RPCJobRegistry
testnet8200https://chat-api.testnet.lightchain.aihttps://rpc.testnet.lightchain.ai0x531b3A87c5D785441B9cF55b98169F20FD9056a7
mainnet9200https://chat-api.mainnet.lightchain.ai (provisional)https://rpc.mainnet.lightchain.ai0xfB15F90298e4CcD7106E76fFB5e520315cC42B0b
The mainnet Developer API is not live yet: mainnet calls fail until it is, and its URL may change. For a local network, pass your own { chainId, apiUrl, rpcUrl, jobRegistry } as network.