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

# Operate a Deployed Agreement

> Read a deployed agreement, submit signed inputs, and confirm state and input-history changes.

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

Operate a deployed agreement by reading its current state, choosing a valid authored input, signing that input, submitting it, and then rereading state and input history.

For TypeScript integrations, use the SDK operation loop first. Endpoint paths later in this page use full API paths under `/v0` for raw HTTP and reference context.

<Note>
  Input submission requires a `walletClient` that can sign with the submitting account and a `publicClient` connected to the agreement chain. The signing wallet must be allowed by the [authored input `issuer`](/workflow/author-agreement-json#author-states-inputs-issuers-and-transitions-as-workflow); otherwise the signed input may be well-formed but invalid for the agreement lifecycle. For automated tests that only need signatures, see [Create a test-only wallet client](/sdks/typescript-client#create-a-test-only-wallet-client).
</Note>

<Note>
  This page shows the API-assisted operation path. The same authorization model is grounded in EIP-712 signed inputs and the onchain execution engine.

  Applications that need direct onchain operation should refer to the [EIP-712 Signing Reference](/reference/eip-712-signing) and [onchain architecture notes](/system-architecture/on-chain) for typed data, contract addresses, and deployment details.
</Note>

## SDK operation loop

<Steps>
  <Step title="Read the agreement and current state">
    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    const agreementRecord = await client.getAgreement(agreementId);
    const current = await client.getAgreementState(agreementId);
    ```

    Use the hosted agreement record for address, `chainId`, participant, observer, and stored agreement JSON context. Use current state to decide which authored inputs are candidates next.
  </Step>

  <Step title="Choose and sign a valid input">
    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { submitAgreementInputWithPermit } from '@cns-labs/agreements-api-client';
    import type { AgreementJson } from '@cns-labs/agreements-protocol-evm';

    const inputRecord = await submitAgreementInputWithPermit({
      client,
      agreementId,
      walletClient,
      publicClient,
      chainId: agreementRecord.chainId,
      agreementContractAddress: agreementRecord.address!,
      agreement: agreementRecord.json as AgreementJson,
      inputId: 'submitInvoice',
      values,
    });
    ```

    Confirm that the input exists, the current state accepts it, the values match the input schema, the signing wallet is allowed by the input `issuer`, and `publicClient` is connected to `agreementRecord.chainId` before submitting.
  </Step>

  <Step title="Reread state and history">
    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    const next = await client.getAgreementState(agreementId);
    const inputsPage = await client.listAgreementInputs(agreementId, { limit: 25 });
    ```

    Use state for the current lifecycle position and input history for audit and chronology.
  </Step>
</Steps>

`publicClient` must be connected to the target chain/RPC. Pass `agreementRecord.chainId` so the helper can reject a mismatched client chain before requesting a signature. Low-level `signAgreementInputPermit(...)` requires an explicit deadline; high-level `submitAgreementInputWithPermit(...)` defaults to `computeDefaultDeadlineSeconds()`.

## Raw operation loop

<Steps>
  <Step title="Read the agreement record">
    Use `GET /v0/agreements/{id}` when your product needs the hosted agreement record and context around it.

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "data": {
        "id": "agr_123",
        "status": "Deployed",
        "address": "0x3333333333333333333333333333333333333333",
        "displayName": "Advisory Retainer",
        "chainId": 59141,
        "participants": [
          {
            "variableKey": "serviceProviderRepresentative",
            "walletAddress": "0x1111111111111111111111111111111111111111"
          },
          {
            "variableKey": "clientRepresentative",
            "walletAddress": "0x2222222222222222222222222222222222222222",
            "email": "client@example.com"
          }
        ],
        "observers": [
          "legal@example.com",
          "ops@example.com"
        ]
      },
      "meta": {
        "apiVersion": "v0",
        "requestId": "req_123"
      }
    }
    ```
  </Step>

  <Step title="Read current state">
    Use `GET /v0/agreements/{id}/state` to decide which authored inputs are candidates next.

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "data": {
        "status": "Deployed",
        "state": "WORK_IN_PROGRESS"
      },
      "meta": {
        "apiVersion": "v0",
        "requestId": "req_123"
      }
    }
    ```

    Interpret the state against the authored agreement JSON.
  </Step>

  <span id="choose-a-valid-authored-input" />

  <Step title="Choose a valid authored input">
    Confirm that the input exists, the current state accepts it, the values match the input schema, and the signer is allowed by the input `issuer`.

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "inputs": {
        "submitInvoice": {
          "type": "VerifiedCredentialEIP712",
          "schema": "verified-credential-eip712.schema.json",
          "displayName": "Submit Invoice",
          "data": {
            "retainerBalanceBeforeInvoice": {
              "type": "uint256",
              "name": "Retainer Balance Before Invoice"
            },
            "invoiceLineItems": {
              "type": "string",
              "subtype": "invoice-csv",
              "name": "Invoice Line Items"
            },
            "submitInvoiceComment": "${variables.submitInvoiceComment}"
          },
          "issuer": "${variables.serviceProviderRepresentative.value}"
        }
      },
      "transitions": [
        {
          "from": "WORK_IN_PROGRESS",
          "to": "INVOICE_SUBMITTED",
          "conditions": [
            {
              "type": "isValid",
              "input": "submitInvoice"
            }
          ]
        }
      ]
    }
    ```
  </Step>

  <Step title="Sign and submit the input">
    Submit the signed input to `POST /v0/agreements/{id}/input`. The request body does not carry a separate `chainId`; the API uses the deployed agreement record. When constructing the EIP-712 signature, use the record's `chainId` in the typed-data domain and connect your RPC client to that same chain.

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "inputId": "submitInvoice",
      "values": {
        "retainerBalanceBeforeInvoice": 1000,
        "invoiceLineItems": "2026-04-01,Advisory services,10,100,1000",
        "submitInvoiceComment": "Invoice submitted for April services."
      },
      "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.
  </Step>

  <Step title="Reread state and history">
    After a successful submission, call `GET /v0/agreements/{id}/state` and `GET /v0/agreements/{id}/inputs` to confirm where the agreement landed and what was recorded.
  </Step>
</Steps>

## Understand the input record

When `POST /v0/agreements/{id}/input` succeeds, the API returns an envelope with the input record in `data`. SDK helpers unwrap `data` and return the input record directly.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "data": {
    "agreementId": "agr_123",
    "agreementAddress": "0x3333333333333333333333333333333333333333",
    "chainId": 59141,
    "inputId": "submitInvoice",
    "values": {
      "retainerBalanceBeforeInvoice": 1000,
      "invoiceLineItems": "2026-04-01,Advisory services,10,100,1000",
      "submitInvoiceComment": "Invoice submitted for April services."
    },
    "txHash": "0x4444444444444444444444444444444444444444444444444444444444444444",
    "payload": "0x...",
    "status": "MINED",
    "blockNumber": 123456,
    "createdAt": "2026-04-27T16:10:00.000Z",
    "updatedAt": "2026-04-27T16:11:00.000Z"
  },
  "meta": {
    "apiVersion": "v0",
    "requestId": "req_123"
  }
}
```

The input record proves the event was accepted and recorded. It does not replace reading current state when your product needs to know where the lifecycle is now.

## Read input history

Use `GET /v0/agreements/{id}/inputs` to inspect recorded submissions. The response is paged; use `limit` and `cursor` to move through history. Add `userId`, `inputId`, or `status` filters when you need a narrower audit view.

You can also filter by `createdAt` or `updatedAt` with `gt`, `gte`, `lt`, and `lte` operators, and sort by `createdAt` or `updatedAt`. Only one sort field is supported.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --globoff -sS "$BASE_URL/v0/agreements/agr_123/inputs?status=MINED&createdAt[gte]=2026-05-01T00:00:00.000Z&sort[createdAt]=desc&limit=25" \
  -H "X-API-Key: $API_KEY"
```

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "data": [
    {
      "agreementId": "agr_123",
      "agreementAddress": "0x3333333333333333333333333333333333333333",
      "chainId": 59141,
      "inputId": "submitInitialPaymentProof",
      "values": {
        "awaitingPaymentPaymentLink": "https://example.com/tx/0xaaa",
        "awaitingPaymentComment": "Initial payment completed."
      },
      "txHash": "0x1111111111111111111111111111111111111111111111111111111111111111",
      "payload": "0x...",
      "status": "MINED",
      "blockNumber": 123450,
      "createdAt": "2026-04-27T16:05:00.000Z",
      "updatedAt": "2026-04-27T16:06:00.000Z"
    },
    {
      "agreementId": "agr_123",
      "agreementAddress": "0x3333333333333333333333333333333333333333",
      "chainId": 59141,
      "inputId": "submitInvoice",
      "values": {
        "retainerBalanceBeforeInvoice": 1000,
        "invoiceLineItems": "2026-04-01,Advisory services,10,100,1000",
        "submitInvoiceComment": "Invoice submitted for April services."
      },
      "txHash": "0x4444444444444444444444444444444444444444444444444444444444444444",
      "payload": "0x...",
      "status": "MINED",
      "blockNumber": 123456,
      "createdAt": "2026-04-27T16:10:00.000Z",
      "updatedAt": "2026-04-27T16:11:00.000Z"
    }
  ],
  "pageInfo": {
    "limit": 25,
    "nextCursor": null,
    "totalCount": 2
  },
  "meta": {
    "apiVersion": "v0",
    "requestId": "req_123"
  }
}
```

## Handle state synchronization

<Note>
  You may see an input record with `status: "MINED"` before the state view reflects the new lifecycle position. Reread state when your product needs certainty about the current position, and use input history for audit and chronology.
</Note>

## Helper boundaries

`submitAgreementInputWithPermit(...)` signs the input payload and calls `client.submitAgreementInput(...)` with the resulting signer, deadline, and signature. Use `signAgreementInputPermit(...)` plus `client.submitAgreementInput(...)` when your application needs to sign first and submit later.

## Why a submission may not move the agreement

When a submission does not advance the agreement, check:

1. the agreement is in the state that accepts the input
2. the submitted `inputId` exists in the authored agreement
3. the submitted `values` match the input schema
4. the signer is allowed by the input `issuer`
5. the transition condition references that input from the current state
6. the signature has not expired and was [generated for this exact payload](/reference/eip-712-signing)
7. the signature was generated for the deployed agreement's `chainId` and contract address

## Related pages

* [Deploy an Agreement](/workflow/deploy-an-agreement)
* [EIP-712 Signing Reference](/reference/eip-712-signing)
* [Errors and troubleshooting](/reference/errors-and-troubleshooting)
