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:
| 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.
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 chain where the agreement factory or deployed agreement contract is used. |
| 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.
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:
- you have an API key for authenticated requests
- you have a
walletClient that can sign with the intended account
- you have a
publicClient connected to the target chain
- the wallet chain matches the deployment or agreement chain
- the signer is appropriate for the deployment or input issuer
- 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. |
Main client methods
| Method | Use |
|---|
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.
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
Related pages