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.

Deployment combines authored agreement JSON with live initValues, participant wallet mappings, optional observers, and a signed EIP-712 permit so the API can create a live agreement. Structural validation checks the authored agreement. Deployment preflight checks the assembled deployment request. Deployment with permit creates the agreement.
Do not sign deployment permits against raw caller input when participant mappings change values that are hashed or encoded into the signed message. Sign the effective post-mapping values returned by deployment preflight.

SDK deployment path

For TypeScript integrations, use the SDK for both deployment preflight and permit signing.
Before deployment, make sure your integration has a walletClient for the deploying signer and a publicClient connected to the target chain/RPC. The signer must be able to produce an EIP-712 signature; the SDK cannot deploy from a wallet address alone. For automated tests that only need signatures, see Create a test-only wallet client.
1

Preflight the assembled deployment request

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

console.log(validation.variables);
Review validation.variables, validation.participants, validation.observers, validation.contributors, and validation.warnings before signing. In raw HTTP flows, review the equivalent fields in the preflight response.
2

Sign and deploy with the high-level helper

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

const deployed = await deployAgreementWithPermit({
  client,
  walletClient,
  publicClient,
  agreement,
  displayName: 'Advisory Retainer',
  initValues: validation.variables,
  participants,
  observers,
});
validation.variables contains the effective deployment values the SDK signs after participant mappings are applied. Keep the same participants array in the helper call so hosted agreement context still records the participant mappings. publicClient must be connected to the target chain/RPC. The helper resolves the chain-specific factory context, reads the current permit nonce, signs the permit, and submits deployWithPermit.
Participant-derived values in validation.variables are the effective values the SDK signs for deployment. The participants array still belongs in deployAgreementWithPermit(...) for hosted participant context.
Use the raw request flow below when you need to inspect the exact payload shape or when you are not using the TypeScript client.

Raw deployment flow

1
2

Assemble deployment context

Provide the authored agreement, initValues for values required at initialization, participants for participant-role wallet mappings, and optional observers. Preflight uses these fields; deployment adds displayName and signed permit fields.Send the full authored agreement JSON in agreement. The nested agreement below is shortened to its top-level sections for readability, but the request shape includes the required agreement field.
{
  "agreement": {
    "metadata": {
      "templateId": "did:template:service-retainer-manual-balance-v0-1",
      "name": "Service Retainer"
    },
    "variables": {
      "serviceProviderRepresentative": {
        "type": "address",
        "subtype": "participant",
        "validation": {
          "required": true
        }
      },
      "clientRepresentative": {
        "type": "address",
        "subtype": "participant",
        "validation": {
          "required": true
        }
      }
    },
    "content": {
      "type": "md",
      "data": "..."
    },
    "execution": {
      "initialize": {
        "initialState": "AWAITING_PAYMENT",
        "data": {}
      },
      "states": {},
      "inputs": {},
      "transitions": []
    }
  },
  "initValues": {
    "retainerTitle": "Advisory Retainer",
    "retainerDescription": "Monthly strategic support with invoice-driven replenishment.",
    "serviceProviderName": "Provider LLC",
    "clientName": "Client Inc",
    "retainerCeiling": 1000,
    "retainerFloor": 200,
    "paymentInstructions": "Wire funds using the invoice instructions."
  },
  "participants": [
    {
      "variableKey": "serviceProviderRepresentative",
      "walletAddress": "0x1111111111111111111111111111111111111111"
    },
    {
      "variableKey": "clientRepresentative",
      "walletAddress": "0x2222222222222222222222222222222222222222",
      "email": "client@example.com"
    }
  ],
  "observers": [
    "legal@example.com",
    "ops@example.com"
  ]
}
Participant wallet identities belong in participants, not duplicated in initValues, when the agreement models them as participant variables.
3
4

Preflight the deployment request

Call POST /v0/agreements/validate before signing.
curl -sS "$BASE_URL/v0/agreements/validate" \
  -X POST \
  -H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
  --data @deployment-preflight.json
Deployment preflight checks authored agreement JSON, initValues, participant mappings, observers, and normalized variables. It does not deploy the agreement and does not validate permit signatures.
5

Review normalized values

The preflight response includes participant keys, normalized participants, normalized observers, contributors, warnings, and effective deployment variables.
{
  "templateId": "did:template:service-retainer-manual-balance-v0-1",
  "participantVariableKeys": [
    "serviceProviderRepresentative",
    "clientRepresentative"
  ],
  "participants": [
    {
      "variableKey": "serviceProviderRepresentative",
      "walletAddress": "0x1111111111111111111111111111111111111111"
    },
    {
      "variableKey": "clientRepresentative",
      "walletAddress": "0x2222222222222222222222222222222222222222",
      "email": "client@example.com"
    }
  ],
  "observers": [
    "legal@example.com",
    "ops@example.com"
  ],
  "variables": {
    "serviceProviderRepresentative": "0x1111111111111111111111111111111111111111",
    "clientRepresentative": "0x2222222222222222222222222222222222222222",
    "retainerTitle": "Advisory Retainer"
  },
  "contributors": [
    "0x1111111111111111111111111111111111111111",
    "0x2222222222222222222222222222222222222222"
  ],
  "warnings": []
}
Resolve warnings before signing. If participant mappings changed any value included in the permit, sign the post-mapping values from variables.
6

Sign the deployment permit

The wallet in signer authorizes deployment by signing EIP-712 typed data. The API uses that authorization to submit the transaction. For direct API deploy, the hosted record owner comes from the authenticated API principal’s primary wallet and may differ from signer.
{
  "signer": "0x1111111111111111111111111111111111111111",
  "deadline": 1776219513,
  "signature": {
    "v": 27,
    "r": "0x...",
    "s": "0x..."
  }
}
The TypeScript client uses a one-hour default permit lifetime through computeDefaultDeadlineSeconds(). Use a shorter deadline if your integration requires a tighter replay window, and regenerate the signature whenever the deadline expires.If you construct the deploy permit typed data directly, read the current nonce from AgreementFactory.nonces(signer) on the target chain through a live RPC before signing. Do not hardcode 0; a successful deploy permit consumes the nonce, so the same signature cannot be reused after deployment or any other signer nonce change.
7

Deploy with the permit

Submit the signed request to POST /v0/agreements/deploy-with-permit.Build deploy-with-permit.json from the same full agreement object and deployment context you preflighted, plus displayName and the permit fields.
{
  "agreement": {
    "metadata": {
      "templateId": "did:template:service-retainer-manual-balance-v0-1",
      "name": "Service Retainer"
    },
    "variables": {
      "serviceProviderRepresentative": {
        "type": "address",
        "subtype": "participant",
        "validation": {
          "required": true
        }
      },
      "clientRepresentative": {
        "type": "address",
        "subtype": "participant",
        "validation": {
          "required": true
        }
      }
    },
    "content": {
      "type": "md",
      "data": "..."
    },
    "execution": {
      "initialize": {
        "initialState": "AWAITING_PAYMENT",
        "data": {}
      },
      "states": {},
      "inputs": {},
      "transitions": []
    }
  },
  "displayName": "Advisory Retainer",
  "initValues": {
    "retainerTitle": "Advisory Retainer",
    "retainerDescription": "Monthly strategic support with invoice-driven replenishment.",
    "serviceProviderName": "Provider LLC",
    "clientName": "Client Inc",
    "retainerCeiling": 1000,
    "retainerFloor": 200,
    "paymentInstructions": "Wire funds using the invoice instructions."
  },
  "participants": [
    {
      "variableKey": "serviceProviderRepresentative",
      "walletAddress": "0x1111111111111111111111111111111111111111"
    },
    {
      "variableKey": "clientRepresentative",
      "walletAddress": "0x2222222222222222222222222222222222222222",
      "email": "client@example.com"
    }
  ],
  "observers": [
    "legal@example.com",
    "ops@example.com"
  ],
  "signer": "0x1111111111111111111111111111111111111111",
  "deadline": 1776219513,
  "signature": {
    "v": 27,
    "r": "0x...",
    "s": "0x..."
  }
}
curl -sS "$BASE_URL/v0/agreements/deploy-with-permit" \
  -X POST \
  -H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
  --data @deploy-with-permit.json
8

Inspect the deployed record

A successful response is an agreement record.
{
  "id": "agr_123",
  "status": "Deployed",
  "address": "0x3333333333333333333333333333333333333333",
  "chainId": 59141,
  "owner": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "participants": [
    {
      "variableKey": "serviceProviderRepresentative",
      "walletAddress": "0x1111111111111111111111111111111111111111"
    },
    {
      "variableKey": "clientRepresentative",
      "walletAddress": "0x2222222222222222222222222222222222222222"
    }
  ],
  "observers": [
    "legal@example.com",
    "ops@example.com"
  ],
  "state": "AWAITING_PAYMENT"
}
The top-level owner is the hosted record owner associated with the authenticated API principal. Carry forward the agreement ID, deployed address, chain ID, hosted record owner, participant context, observer context, and current state.
After deployment, the agreement’s core definition is fixed. This is intentional: the deployed agreement becomes a shared operational source of truth that parties can inspect and rely on.If the agreement needs to change, model the change explicitly through the agreement itself, deploy a new agreement, or use an amendment pattern appropriate to the application.

Helper boundaries

The higher-level deployAgreementWithPermit(...) helper signs and submits the deployment request, but it does not replace the deployment preflight review step. Use client.validateDeployment(...) first when participant mappings or deployment values affect the permit payload. Low-level signDeployWithPermit(...) requires an explicit deadline; high-level deployAgreementWithPermit(...) defaults to computeDefaultDeadlineSeconds().

Keep deployment boundaries clear

BoundaryWhat to remember
POST /v0/agreements/validate-templateChecks authored agreement JSON only.
POST /v0/agreements/validateChecks agreement JSON plus deployment context and normalized variables.
POST /v0/agreements/deploy-with-permitCreates the live agreement with signed authorization.
participantsMaps participant-role variables to wallet addresses.
observersAdds optional email context; it is not participant identity.
deployment permitAuthorizes deployment; it does not submit post-deploy inputs.