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

# Deploy an Agreement

> Turn structurally valid agreement JSON into a live agreement with deployment values, participant mappings, preflight checks, and EIP-712 authorization.

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

Deployment combines authored agreement JSON with a target `chainId`, live `initValues`, participant wallet mappings, optional observers, and a [signed EIP-712 permit](/reference/eip-712-signing) so the API can create a live agreement.

[Structural validation](/workflow/validate-agreement-structure) checks the authored agreement. Deployment preflight checks the assembled deployment request. Deployment with permit creates the agreement.

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

<Note>
  Each hosted API environment can support multiple agreement deployment chains at the same time. The `testnet` environment supports Linea Sepolia, Ethereum Sepolia, and Base Sepolia; the `production` environment supports Linea Mainnet and Base Mainnet. Choose a supported `chainId` for every deployment preflight and deploy request, and use a `publicClient` connected to that same chain before signing.
</Note>

## SDK deployment path

For TypeScript integrations, use the SDK for both deployment preflight and permit signing.

<Note>
  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](/sdks/typescript-client#create-a-test-only-wallet-client).
</Note>

<Steps>
  <Step title="Preflight the assembled deployment request">
    <Tabs>
      <Tab title="SDK">
        ```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);
        ```
      </Tab>

      <Tab title="HTTP">
        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        curl -sS "$BASE_URL/v0/agreements/validate" \
          -X POST \
          -H "Content-Type: application/json" \
        -H "X-API-Key: $API_KEY" \
          --data @deployment-preflight.json
        ```
      </Tab>
    </Tabs>

    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. Keep the selected `chainId` with the deployment request so the preflight, permit signature, and deploy call target the same chain.
  </Step>

  <Step title="Sign and deploy with the high-level helper">
    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { deployAgreementWithPermit } from '@cns-labs/agreements-api-client';

    const deployed = await deployAgreementWithPermit({
      client,
      walletClient,
      publicClient,
      chainId,
      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 selected chain/RPC. The helper resolves the chain-specific factory context, reads the current permit nonce, signs the permit, and submits `deployWithPermit`.
  </Step>
</Steps>

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

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

<Steps>
  <span id="assemble-deployment-context" />

  <Step title="Assemble deployment context">
    Provide the authored `agreement`, target `chainId`, `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.

    `chainId` must be one of the agreement deployment chains supported by the target API environment. Include it in both deployment preflight and deploy-with-permit requests so validation, EIP-712 signing, and transaction submission all target the same chain.

    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.

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "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": []
        }
      },
      "chainId": 59141,
      "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.
  </Step>

  <span id="preflight-the-deployment-request" />

  <Step title="Preflight the deployment request">
    Call `POST /v0/agreements/validate` before signing.

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    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.
  </Step>

  <Step title="Review normalized values">
    The raw HTTP preflight response is wrapped in a `data` envelope. `client.validateDeployment(...)` unwraps `data` and returns the preflight summary directly.

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "data": {
        "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": []
      },
      "meta": {
        "apiVersion": "v0",
        "requestId": "req_123"
      }
    }
    ```

    Resolve warnings before signing. If participant mappings changed any value included in the permit, sign the post-mapping values from `data.variables`.
  </Step>

  <Step title="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`.

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "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.
  </Step>

  <Step title="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.

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "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",
      "chainId": 59141,
      "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..."
      }
    }
    ```

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    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
    ```
  </Step>

  <Step title="Inspect the deployed record">
    A successful raw HTTP response returns an envelope with the deployed agreement record in `data`. SDK deployment helpers unwrap `data` and return the agreement record directly.

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "data": {
        "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"
      },
      "meta": {
        "apiVersion": "v0",
        "requestId": "req_123"
      }
    }
    ```

    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. Use the returned `chainId` for later agreement reads, input signing, and chain-filtered agreement lists.
  </Step>
</Steps>

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

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

| Boundary                                 | What to remember                                                                                                    |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `POST /v0/agreements/validate-template`  | Checks authored agreement JSON only.                                                                                |
| `POST /v0/agreements/validate`           | Checks agreement JSON plus deployment context and normalized variables.                                             |
| `POST /v0/agreements/deploy-with-permit` | Creates the live agreement with signed authorization.                                                               |
| `chainId`                                | Selects the target deployment chain; include the same value in preflight, signing, and deploy-with-permit requests. |
| `participants`                           | Maps participant-role variables to wallet addresses.                                                                |
| `observers`                              | Adds optional email context; it is not participant identity.                                                        |
| deployment permit                        | Authorizes deployment; it does not submit post-deploy inputs.                                                       |

## Related pages

* [Validate Agreement Structure](/workflow/validate-agreement-structure)
* [Operate a Deployed Agreement](/workflow/operate-a-deployed-agreement)
* [EIP-712 Signing Reference](/reference/eip-712-signing)
* [Errors and troubleshooting](/reference/errors-and-troubleshooting)
