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

# TypeScript client reference

> Reference the `@cns-labs/agreements-api-client` constructor, methods, signing helpers, diagnostics, path helpers, and exports.

For the complete documentation index, see [llms.txt](https://docs.shodai.network/llms.txt).

Use [Quickstart with TypeScript SDK](/sdks/quickstart-with-typescript-sdk) for first setup. Use this page after connection to choose client methods, signing helpers, diagnostics, path helpers, and exports.

MCP users who do not already have a wallet or signing service can also use this page to set up the TypeScript SDK with `viem` as a local testnet signing harness for typed data prepared by [Quickstart with MCP](/sdks/quickstart-with-mcp).

Use literal SDK symbols exactly as exported by the package, including `ApiClient`, `AgreementsApiError`, `agreementsApiPaths`, and `API_BASE_PATH`.

The package targets Node `>=18`.

## Install

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npm install @cns-labs/agreements-api-client viem
```

`@cns-labs/agreements-protocol-evm` is installed automatically as a package dependency. Install `viem` in your app when you use permit-signing helpers.

The Agreements API client and the onchain protocol SDK are both published under the `@cns-labs` npm organization. The `agreements-api-playground` sample application uses `@cns-labs/agreements-api-client` for API calls and `@cns-labs/agreements-protocol-evm` for onchain agreement typing and signing support.

## Create a client

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { ApiClient } from '@cns-labs/agreements-api-client';

const client = new ApiClient({
  environment: 'testnet',
  apiKey: process.env.API_KEY,
});

const health = await client.getHealth();
```

Constructor config:

| Field         | Use                                                                                                                       |
| ------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `environment` | Named API environment. Supported values are `testnet` and `production`.                                                   |
| `baseUrl`     | Optional API host base URL override, without `/v0` appended. Use for local proxies, staging hosts, or custom deployments. |
| `apiKey`      | Optional `X-API-Key` value. Most API methods require it.                                                                  |
| `headers`     | Optional header record or header factory merged into each request.                                                        |
| `fetch`       | Optional fetch implementation. Defaults to `globalThis.fetch`.                                                            |

If both `environment` and `baseUrl` are supplied, `baseUrl` wins. `testnet` resolves to `https://test-api.shodai.network`; `production` resolves to `https://api.shodai.network`.

For key provisioning, scopes, entitlements, and `401`/`402`/`403` behavior, see [Authentication](/authentication).

## Wallet and RPC requirements

The API key authenticates requests. Deploying agreements and submitting agreement inputs also require a wallet your integration controls because those workflows depend on EIP-712 signatures.

| Requirement        | Why it matters                                                                                      |
| ------------------ | --------------------------------------------------------------------------------------------------- |
| `walletClient`     | Controls the signing account and produces EIP-712 signatures. A plain wallet address is not enough. |
| `publicClient`     | Reads target-chain context, factory or agreement contract state, and current permit nonces.         |
| Target chain/RPC   | Must match the selected deployment chain or the deployed agreement's chain.                         |
| Signer eligibility | For inputs, the signing wallet must be allowed by the authored input `issuer`.                      |

### Create a test-only wallet client

For automated tests that only need EIP-712 signatures, create an ephemeral wallet with `viem`. This wallet does not need gas when it only signs permits. Do not use this pattern for production wallets, and do not commit generated private keys.

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { createPublicClient, createWalletClient, http } from 'viem';
import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
import { lineaSepolia } from 'viem/chains';

const account = privateKeyToAccount(generatePrivateKey());

const walletClient = createWalletClient({
  account,
  chain: lineaSepolia,
  transport: http(process.env.RPC_URL),
});

const publicClient = createPublicClient({
  chain: lineaSepolia,
  transport: http(process.env.RPC_URL),
});
```

The `testnet` API environment supports Linea Sepolia (`59141`), Ethereum Sepolia (`11155111`), and Base Sepolia (`84532`) for agreement deployments. The `production` API environment supports Linea Mainnet (`59144`) and Base Mainnet (`8453`). Choose the `chainId` explicitly for deployment preflight and deploy requests, then reuse the deployed agreement record's `chainId` when signing inputs.

Use `account.address` in participant mappings when the test wallet needs to deploy an agreement or submit an input for a participant role.

Before calling `deployAgreementWithPermit(...)` or `submitAgreementInputWithPermit(...)`, confirm that:

1. you have an API key for authenticated requests
2. you have a `walletClient` that can sign with the intended account
3. you have a `publicClient` connected to the target chain
4. the wallet chain matches the deployment or agreement chain
5. the signer is appropriate for the deployment or input issuer
6. you can regenerate signatures when nonce, deadline, payload, chain, agreement JSON, or values change

## Choose the right SDK surface

| Use case                                                   | Prefer                                                                                                                                    |
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Normal API calls                                           | `ApiClient` methods such as `validateTemplate`, `validateDeployment`, and `getAgreementState`.                                            |
| Deploy with an EIP-712 permit                              | `deployAgreementWithPermit(...)`.                                                                                                         |
| Submit a signed input                                      | `submitAgreementInputWithPermit(...)`.                                                                                                    |
| Sign first, submit later, or customize request composition | `signDeployWithPermit(...)` or `signAgreementInputPermit(...)` plus `client.deployWithPermit(...)` or `client.submitAgreementInput(...)`. |
| Debug HTTP status, headers, or raw body text               | `client.exchangeJson(...)`.                                                                                                               |
| Compose a raw request path safely                          | `agreementsApiPaths`.                                                                                                                     |
| Construct typed data manually without SDK helpers          | [EIP-712 Signing Reference](/reference/eip-712-signing).                                                                                  |

## Main client methods

The public API returns JSON envelopes for authenticated agreement routes. SDK methods that read one resource unwrap `data` and return the resource directly. List methods return the list envelope so your integration can read `data`, `pageInfo`, and `meta`.

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const agreement = await client.getAgreement(agreementId);

const page = await client.listAgreements({ limit: 25 });
console.log(page.data, page.pageInfo.nextCursor, page.meta.requestId);
```

| Method                                       | Use                                                                                                                                              |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `getOpenApiDocument()`                       | Fetch `GET /v0/openapi.json`.                                                                                                                    |
| `getHealth()`                                | Check `GET /v0/health`.                                                                                                                          |
| `listAgreements(params)`                     | List paged agreement summaries visible to the API key. Supports `limit`, `cursor`, `sort`, `chainId`, `state`, and created/updated date filters. |
| `getAgreement(agreementId)`                  | Read one agreement record.                                                                                                                       |
| `validateTemplate(agreement)`                | Validate authored agreement JSON with `POST /v0/agreements/validate-template`.                                                                   |
| `validateDeployment(body)`                   | Preflight deployment context with `POST /v0/agreements/validate`.                                                                                |
| `deployWithPermit(body)`                     | Deploy with a prepared EIP-712 permit.                                                                                                           |
| `getAgreementState(agreementId)`             | Read the current agreement state.                                                                                                                |
| `listAgreementInputs(agreementId, params)`   | Read paged input history. Supports `limit`, `cursor`, `sort`, `userId`, `inputId`, `status`, and created/updated date filters.                   |
| `submitAgreementInput(agreementId, body)`    | Submit a signed input.                                                                                                                           |
| `exchangeJson(method, path, body?)`          | Inspect raw response metadata without throwing for HTTP errors.                                                                                  |
| `request<T>(method, path, body?, okStatus?)` | Low-level JSON request that returns the raw JSON body and throws on unexpected status.                                                           |

List filters use `qs`-style bracket modifiers. For example, `{ createdAt: { gte: '...' }, sort: { createdAt: 'desc' } }` serializes as `createdAt[gte]=...&sort[createdAt]=desc`.

Agreement lists support `chainId`, `state`, `createdAt`, `updatedAt`, `limit`, `cursor`, and one `sort` field from `createdAt`, `updatedAt`, or `displayName`. Input history supports `userId`, `inputId`, `status`, `createdAt`, `updatedAt`, `limit`, `cursor`, and one `sort` field from `createdAt` or `updatedAt`. Date filters support `gt`, `gte`, `lt`, and `lte`; `limit` must be between `1` and `100`.

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const agreementsPage = await client.listAgreements({
  chainId: 59141,
  state: 'AWAITING_PAYMENT',
  createdAt: { gte: '2026-05-01T00:00:00.000Z' },
  sort: { createdAt: 'desc' },
  limit: 25,
});
const nextAgreementPage = agreementsPage.pageInfo.nextCursor
  ? await client.listAgreements({ cursor: agreementsPage.pageInfo.nextCursor, limit: 25 })
  : null;

const inputsPage = await client.listAgreementInputs('agreement-123', {
  userId: 'platform-user-id',
  status: 'MINED',
  updatedAt: { lt: '2026-06-01T00:00:00.000Z' },
  sort: { updatedAt: 'asc' },
  limit: 25,
});
```

## Validate and deploy with helpers

Use deployment preflight before signing when `initValues`, participant mappings, or observers affect the deployment request.

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const chainId = 59141;

const validation = await client.validateDeployment({
  agreement,
  chainId,
  initValues,
  participants,
  observers,
});

console.log(validation.variables);
```

Then use the high-level helper for the normal sign-and-submit path.

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { deployAgreementWithPermit } from '@cns-labs/agreements-api-client';

const agreementRecord = await deployAgreementWithPermit({
  client,
  walletClient,
  publicClient,
  chainId,
  agreement,
  displayName: 'Consulting Agreement',
  initValues: validation.variables,
  participants,
  observers,
});
```

<Warning>
  Participant-derived values in `validation.variables` are the effective values the SDK signs for deployment. Keep the same `participants` array in `deployAgreementWithPermit(...)` so hosted agreement context records the participant mappings.
</Warning>

## Submit signed inputs with helpers

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { submitAgreementInputWithPermit } from '@cns-labs/agreements-api-client';

await submitAgreementInputWithPermit({
  client,
  agreementId: agreementRecord.id,
  walletClient,
  publicClient,
  chainId: agreementRecord.chainId,
  agreementContractAddress,
  agreement,
  inputId: 'partyASignature',
  values,
});
```

`publicClient` must be connected to the target chain/RPC. Input submissions also require `chainId` from the deployed agreement record so the helper can fail before signing if the client is connected to the wrong chain. The signing helpers use `publicClient` to resolve the chain-specific `AgreementFactory` or agreement contract context and to read the current permit nonce before signing.

## Control deadlines and low-level signing

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import {
  computeDefaultDeadlineSeconds,
  signAgreementInputPermit,
  signDeployWithPermit,
} from '@cns-labs/agreements-api-client';
```

`DEFAULT_PERMIT_DEADLINE_SECONDS` is `3600`. `computeDefaultDeadlineSeconds(offsetSeconds = 3600)` returns the current Unix time plus the offset.

High-level `deployAgreementWithPermit(...)` and `submitAgreementInputWithPermit(...)` default the deadline. Low-level `signDeployWithPermit(...)` and `signAgreementInputPermit(...)` require an explicit deadline.

Use low-level signing helpers when your application needs to sign first and submit later, inspect the signature, or compose the request body itself. Use [EIP-712 Signing Reference](/reference/eip-712-signing) only when you need to construct the typed data directly or debug helper behavior.

## Handle API errors

The client throws `AgreementsApiError` when the response status does not match the expected success status.

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { AgreementsApiError } from '@cns-labs/agreements-api-client';

try {
  await client.listAgreements();
} catch (error) {
  if (error instanceof AgreementsApiError) {
    console.error(error.status, error.errorPayload?.error.message ?? error.bodyText);
  }
  throw error;
}
```

`AgreementsApiError` exposes `status`, `bodyText`, `parsedBody`, and `errorPayload`. `errorPayload.error.code` is stable for branching, and `errorPayload.error.requestId` is the value to share when you need support to trace a request.

## Use raw exchange for diagnostics

Use `exchangeJson()` when you need response metadata or raw body text during debugging. It does not throw for HTTP error status codes.

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const response = await client.exchangeJson('GET', '/v0/agreements');

console.log(response.status, response.ok, response.parsedBody);
```

`exchangeJson()` accepts only `GET` and `POST`. It returns `status`, `ok`, `headers`, `bodyText`, and `parsedBody`.

## Use path helpers when composing raw calls

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { agreementsApiPaths } from '@cns-labs/agreements-api-client';

const path = agreementsApiPaths.agreementState('agreement-123');
```

Path helper names are `openapiJson`, `health`, `agreements`, `agreementsValidate`, `agreementsValidateTemplate`, `agreementsDeployWithPermit`, `agreement`, `agreementState`, `agreementInputs`, and `agreementInput`. ID path helpers encode IDs and use `API_BASE_PATH`, which is `/v0`.

## Root exports

The package root exports:

* `ApiClient`
* `AgreementsApiError`
* `extractAgreementsApiErrorMessage`
* `agreementsApiPaths`
* `getExecutionInputIds`
* `API_BASE_PATH`
* `API_ENVIRONMENT_BASE_URLS`
* `API_MAJOR_VERSION`
* `DEFAULT_API_ENVIRONMENT`
* `resolveApiBaseUrl`
* `joinUrl`
* `computeDefaultDeadlineSeconds`
* `DEFAULT_PERMIT_DEADLINE_SECONDS`
* `signDeployWithPermit`
* `signAgreementInputPermit`
* `deployAgreementWithPermit`
* `submitAgreementInputWithPermit`

## Related pages

* [Quickstart with TypeScript SDK](/sdks/quickstart-with-typescript-sdk)
* [Quickstart with MCP](/sdks/quickstart-with-mcp)
* [Run an end-to-end agreement workflow](/examples/end-to-end-workflow)
* [Authentication](/authentication)
* [Validate Agreement Structure](/workflow/validate-agreement-structure)
* [Deploy an Agreement](/workflow/deploy-an-agreement)
* [Operate a Deployed Agreement](/workflow/operate-a-deployed-agreement)
* [EIP-712 Signing Reference](/reference/eip-712-signing)
* [Errors and troubleshooting](/reference/errors-and-troubleshooting)
