@shodai-network/agreements-api-client
v0.3.1
Published
TypeScript client for the Agreements API with built-in permit-signing helpers
Readme
@shodai-network/agreements-api-client
TypeScript client for the Agreements API, including built-in helpers for agreement validation, deployment, inspection, and permit-based input submission.
The package bundles:
ApiClientfor the HTTP API- typed request and response models
- helper utilities for Agreements API paths and execution input IDs
viemhelpers that compose with agreement deployment and input-signing flows
Maintainer workflows such as local builds and publishing live in DEVELOPMENT.md.
Install
Install the client for HTTP-only usage:
npm install @shodai-network/agreements-api-clientAdd viem if you want the built-in wallet signing helpers:
npm install @shodai-network/agreements-api-client viemCreate a Client
import { ApiClient } from '@shodai-network/agreements-api-client';
const client = new ApiClient({
environment: 'testnet',
apiKey: process.env.API_KEY,
});
const health = await client.getHealth();Environment resolution
testnetresolves tohttps://test-api.shodai.networkproductionresolves tohttps://api.shodai.network- The client automatically prefixes requests with
/v0/*
Hosted environments can support multiple agreement deployment chains at once. The testnet environment supports Linea Sepolia (59141), Ethereum Sepolia (11155111), and Base Sepolia (84532); the production environment supports Linea Mainnet (59144) and Base Mainnet (8453). Include a supported chainId when validating and deploying agreements, and use the deployed agreement record's chainId when signing inputs.
Optional baseUrl override
Use baseUrl only when you need to bypass the standard Shodai environment mapping, for example:
- local proxies
- internal gateways
- custom preview or staging deployments
const client = new ApiClient({
environment: 'testnet',
baseUrl: 'http://localhost:8080',
apiKey: process.env.API_KEY,
});When baseUrl is provided, it wins over the environment host mapping.
Authentication and custom headers
apiKeyis sent asX-API-Key- use the API key issued for your API principal by your deployment operator
headerslets you attach correlation IDs, telemetry headers, or other request metadatafetchcan be overridden if your runtime does not provide a compatible globalfetch
Response Envelopes and Query Results
The Agreements API wraps successful JSON responses in an envelope so every response can carry request metadata:
{
"data": {},
"meta": {
"apiVersion": "v0",
"requestId": "req_123"
}
}The SDK unwraps single-resource responses for convenience. For example, getAgreement(), getAgreementState(), validateTemplate(), validateDeployment(), deployWithPermit(), and submitAgreementInput() return the data value directly.
List methods return the full list envelope because paging information is part of the result:
const agreementsPage = await client.listAgreements({ limit: 25 });
console.log(agreementsPage.data);
console.log(agreementsPage.pageInfo.nextCursor);
console.log(agreementsPage.meta.requestId);List responses use:
data: the current page of recordsmeta.apiVersion: the API version that produced the responsemeta.requestId: the request ID to include in support or debugging reportspageInfo.limit: the page size applied by the APIpageInfo.nextCursor: the cursor for the next page, ornullwhen there is no next pagepageInfo.totalCount: the total matching record count, when the API includes it
Error responses use an error envelope:
{
"error": {
"code": "unauthorized",
"message": "Missing API key",
"requestId": "req_123"
}
}When an SDK request fails, AgreementsApiError#errorPayload exposes this structured error body when the server returns it.
Choose a Usage Path
Use the HTTP client only when you want to:
- validate templates
- validate deploy payloads
- list or fetch agreements
- inspect state and input history
- submit already-signed permit payloads
Use the viem helpers when you also want the SDK to:
- create the deploy permit signature
- create the execution-input permit signature
- submit the signed payload immediately after signing
Agreement Lifecycle
1. Validate template JSON
Use this when you want to validate only the agreement definition:
const templateResult = await client.validateTemplate(agreement);
console.log(templateResult.inputIds);
console.log(templateResult.stateIds);2. Validate a full deployment payload
Use this before deployment when you already know the initial values and participants:
const validation = await client.validateDeployment({
agreement,
chainId: 59141,
initValues,
participants,
observers,
});
console.log(validation.participantVariableKeys);
console.log(validation.warnings);3. Deploy with a pre-signed permit
Use this if your app signs permits itself and only needs the HTTP client to send the request:
const deployed = await client.deployWithPermit({
agreement,
displayName: 'Consulting Agreement',
chainId: 59141,
signer,
deadline,
signature,
initValues,
participants,
observers,
});
console.log(deployed.id);
console.log(deployed.address);4. Sign and deploy with viem
Use the helper flow when you have a walletClient and publicClient available:
import {
computeDefaultDeadlineSeconds,
deployAgreementWithPermit,
} from '@shodai-network/agreements-api-client';
const agreementRecord = await deployAgreementWithPermit({
client,
walletClient,
publicClient,
chainId: 59141,
agreement,
displayName: 'Consulting Agreement',
initValues,
participants,
observers,
deadline: computeDefaultDeadlineSeconds(),
});This flow requires:
- a connected wallet
- a selected
chainIdsupported by the target API environment - chain configuration compatible with that selected deployment chain
viemwalletClientandpublicClient
5. Inspect agreements after deployment
const agreementsPage = await client.listAgreements({
chainId: 59141,
state: 'AWAITING_PAYMENT',
createdAt: { gte: '2026-05-01T00:00:00.000Z' },
sort: { createdAt: 'desc' },
limit: 25,
});
const agreementRecord = await client.getAgreement(agreementsPage.data[0].id);
const state = await client.getAgreementState(agreementRecord.id);
const inputsPage = await client.listAgreementInputs(agreementRecord.id, {
sort: { updatedAt: 'desc' },
limit: 25,
});
console.log(state.state);
console.log(inputsPage.data.length);listAgreements() returns agreement summaries. Use getAgreement(agreementsPage.data[index].id) when you need the full agreement JSON, participants, observers, variables, or on-chain context.
Agreement list filters:
state: current agreement state, such asAWAITING_PAYMENTchainId: agreement deployment chaincreatedAtandupdatedAt: date filters withgt,gte,lt, andltesort: one sort field:createdAt,updatedAt, ordisplayNamelimit: page sizecursor: cursor returned bypageInfo.nextCursor
Input history filters:
userId: platform user ID associated with the submissioninputId: input ID defined in the agreement JSONstatus: input submission status:PENDING,MINED, orFAILEDcreatedAtandupdatedAt: date filters withgt,gte,lt, andltesort: one sort field:createdAtorupdatedAtlimit: page sizecursor: cursor returned bypageInfo.nextCursor
Nested filters and sorts are encoded as query parameters such as createdAt[gte]=2026-05-01T00%3A00%3A00.000Z and sort[createdAt]=desc.
6. Discover execution input IDs
If you are rendering an input-submission UI from agreement JSON, use getExecutionInputIds():
import { getExecutionInputIds } from '@shodai-network/agreements-api-client';
const inputIds = getExecutionInputIds(agreement);
console.log(inputIds);This reads execution.inputs keys from the parsed agreement document.
7. Submit a signed execution input
If your app already has a permit signature:
const inputRecord = await client.submitAgreementInput(agreementId, {
inputId: 'partyASignature',
values,
signer,
deadline,
signature,
});
console.log(inputRecord.status);Or sign and submit in one step with viem:
import {
computeDefaultDeadlineSeconds,
submitAgreementInputWithPermit,
} from '@shodai-network/agreements-api-client';
const inputRecord = await submitAgreementInputWithPermit({
client,
agreementId: agreementRecord.id,
walletClient,
publicClient,
chainId: agreementRecord.chainId,
agreementContractAddress,
agreement,
inputId: 'partyASignature',
values,
deadline: computeDefaultDeadlineSeconds(),
});Agreement JSON and Types
The client accepts agreement JSON as Record<string, unknown> for HTTP transport. This keeps the API client usable even if your app manages agreement JSON outside this package.
If your application already uses typed agreement objects, the viem helper layer is designed to work with agreement data compatible with AgreementJson from @shodai-network/agreements-protocol-evm.
In practice:
- use plain JSON objects for validation and transport-centric API calls
- use typed agreement objects when you want stronger guarantees around signing and execution flows
Useful Methods
getOpenApiDocument()to inspect the raw OpenAPI document exposed by the gatewaygetHealth()to check gateway reachabilitylistAgreements()andgetAgreement()to browse agreement summaries or load full agreement recordslistAgreementInputs()to inspect paged input history for an agreementvalidateTemplate()andvalidateDeployment()before deploydeployWithPermit()andsubmitAgreementInput()for HTTP-only signed callsexchangeJson()for low-level debugging with full status, headers, and raw body access
Reference Implementation
For a complete browser workflow, see the playground:
The hosted playground is available at https://developers.shodai.network/api-playground.
The playground demonstrates:
- validating inline agreement JSON
- signing and posting
deploy-with-permit - loading agreement state and cached inputs
- signing and posting
/agreements/:id/input
Runtime Notes
- Node
>=18 - Uses global
fetchby default; passfetchin the constructor if your runtime needs a custom implementation - The package publishes a single root entrypoint for both API methods and signing helpers
