Skip to main content

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.

Use the TypeScript client as the default integration surface for the Agreements API. The workflow pages show when to use each method; this page explains what the package exposes and how to choose between high-level helpers, low-level helpers, and raw diagnostics. Use literal SDK symbols exactly as exported by the package, including ApiClient, AgreementsApiError, agreementsApiPaths, and API_BASE_PATH. The package targets Node >=18.

Install

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

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:
FieldUse
environmentNamed API environment. Supported values are testnet and production.
baseUrlOptional API host base URL override, without /v0 appended. Use for local proxies, staging hosts, or custom deployments.
apiKeyOptional X-API-Key value. Most API methods require it.
headersOptional header record or header factory merged into each request.
fetchOptional 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.

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.
RequirementWhy it matters
walletClientControls the signing account and produces EIP-712 signatures. A plain wallet address is not enough.
publicClientReads target-chain context, factory or agreement contract state, and current permit nonces.
Target chain/RPCMust match the chain where the agreement factory or deployed agreement contract is used.
Signer eligibilityFor 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.
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),
});
Shodai test and development deployments use Linea Sepolia (59141); production deployments use Linea Mainnet (59144). 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 casePrefer
Normal API callsApiClient methods such as validateTemplate, validateDeployment, and getAgreementState.
Deploy with an EIP-712 permitdeployAgreementWithPermit(...).
Submit a signed inputsubmitAgreementInputWithPermit(...).
Sign first, submit later, or customize request compositionsignDeployWithPermit(...) or signAgreementInputPermit(...) plus client.deployWithPermit(...) or client.submitAgreementInput(...).
Debug HTTP status, headers, or raw body textclient.exchangeJson(...).
Compose a raw request path safelyagreementsApiPaths.
Construct typed data manually without SDK helpersEIP-712 Signing Reference.

Main client methods

MethodUse
getOpenApiDocument()Fetch GET /v0/openapi.json.
getHealth()Check GET /v0/health.
listAgreements(params)List agreements visible to the API key, with optional status of Draft or Deployed.
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 input history, optionally filtered by platform user ID.
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 throws on unexpected status.
await client.listAgreements({ status: 'Draft' });
await client.listAgreementInputs('agreement-123', { userId: 'platform-user-id' });

Validate and deploy with helpers

Use deployment preflight before signing when initValues, participant mappings, or observers affect the deployment request.
const validation = await client.validateDeployment({
  agreement,
  initValues,
  participants,
  observers,
});

console.log(validation.variables);
Then use the high-level helper for the normal sign-and-submit path.
import { deployAgreementWithPermit } from '@cns-labs/agreements-api-client';

const agreementRecord = await deployAgreementWithPermit({
  client,
  walletClient,
  publicClient,
  agreement,
  displayName: 'Consulting Agreement',
  initValues: validation.variables,
  participants,
  observers,
});
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.

Submit signed inputs with helpers

import { submitAgreementInputWithPermit } from '@cns-labs/agreements-api-client';

await submitAgreementInputWithPermit({
  client,
  agreementId: agreementRecord.id,
  walletClient,
  publicClient,
  agreementContractAddress,
  agreement,
  inputId: 'partyASignature',
  values,
});
publicClient must be connected to the target chain/RPC. The signing helpers use it 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

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 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.
import { AgreementsApiError } from '@cns-labs/agreements-api-client';

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

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

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