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.

Author agreement JSON by modeling the real business workflow first, then expressing that workflow through variables, content, states, inputs, issuers, transitions, and initialization.

What is an agreement?

An agreement is an authored JSON artifact that combines readable agreement text, the business facts that fill that text, participant roles, and lifecycle rules for what can happen after deployment. The agreement data standard explains the agreement definition model in architectural context. This page focuses on the authoring decisions that turn a real business workflow into that JSON structure.

Start with the workflow

Before writing JSON, answer these questions in plain language:
  1. Who participates?
  2. What facts does the agreement depend on?
  3. What should the agreement say?
  4. What state does it begin in?
  5. What can happen later?
  6. Who is allowed to do each later thing?
  7. What changes when those things happen?
When those answers are vague, the JSON usually becomes harder to validate, deploy, and operate.

Programmatic authoring

Agreement definitions are data. They can be authored manually, generated by applications, or produced by agents, as long as they conform to the agreement data standard and target the capabilities of the current onchain execution engine. Programmatic authoring is useful when agreements are created dynamically at runtime, such as agent-to-agent coordination, marketplace interactions, resource-sharing arrangements, or short-lived service agreements. Generated agreements should still be validated, reviewable, and understandable before deployment because humans, applications, and agents may rely on their behavior.

Model only facts that do work

A variable should earn its place in the agreement.
A variable is useful when it…Why it matters
appears in the agreement textReaders and signers need the value.
must exist when the agreement startsDeployment must supply or map the value.
is submitted later as part of an eventOperation needs the value for an input.
helps determine who may actIssuer rules need the value.
helps explain the agreement in interfaces or agentsOperators need the value to understand the workflow.

Make variables understandable

Each variable should explain what it means and how it should be used.
{
  "retainerFloor": {
    "type": "uint256",
    "name": "Retainer Floor",
    "helperText": "Threshold at which retainer topup requested",
    "description": "The minimum amount expected to be held in the retainer from month to month. If an invoice would reduce the retainer below this amount, topup of the retainer should be requested.",
    "validation": {
      "required": true,
      "min": 0
    }
  }
}
The field is useful because it names the number, explains its workflow meaning, and constrains the expected range.

Treat participant addresses as roles

Participant addresses define who the agreement is about and who may act inside it. If a variable represents a participant, mark it as a participant role.
{
  "serviceProviderRepresentative": {
    "type": "address",
    "subtype": "participant",
    "name": "Service Provider Representative",
    "helperText": "Wallet address designating the service provider representative",
    "description": "Participant address used when the service provider representative submits and reviews agreement inputs."
  },
  "clientRepresentative": {
    "type": "address",
    "subtype": "participant",
    "name": "Client Representative",
    "helperText": "Wallet address designating the client representative",
    "description": "Participant address used when the client representative submits and reviews agreement inputs."
  }
}
Those roles later connect to deployment participant mappings and input issuer rules.

Use validation to sharpen clear fields

Validation should make an already-clear field precise. The examples use validation for required values, numeric minimums, and string length.
{
  "awaitingPaymentPaymentLink": {
    "type": "string",
    "subtype": "url",
    "name": "Link to Payment Proof",
    "helperText": "Enter transaction url",
    "description": "Block explorer link for external payment or settlement proof",
    "validation": {
      "required": true,
      "minLength": 1
    }
  }
}
Use this pattern: decide the fact, describe the fact clearly, then add validation that reflects the actual expectation.

Keep content and execution aligned

Agreement content should read like a real document while using the same variables the rest of the agreement depends on.
{
  "content": {
    "type": "md",
    "data": "# ${variables.retainerTitle}\n\n${variables.retainerDescription}\n\n## Participants\n\n- **Service Provider:** ${variables.serviceProviderName}\n- **Client:** ${variables.clientName}"
  }
}
The content should not imply facts or behavior that the variables and lifecycle do not support.

Author states, inputs, issuers, and transitions as workflow

States should be recognizable moments in the business process. Inputs should be real events someone can choose. Issuers should encode who is responsible for each event. Transitions should show how valid events move the lifecycle.
{
  "transitions": [
    {
      "from": "WORK_IN_PROGRESS",
      "to": "INVOICE_SUBMITTED",
      "conditions": [
        {
          "type": "isValid",
          "input": "submitInvoice"
        }
      ]
    }
  ]
}
A lifecycle is strong when someone can read the states, inputs, and transitions together and understand the agreement process. The state/transition model gives the agreement a behavioral map. It helps authors, counterparties, applications, and agents understand how the agreement can progress before it is deployed. This visual representation is not merely illustrative. It reflects the authored behavior: the states the agreement can occupy, the inputs accepted in each state, who may submit those inputs, and the transitions that can follow.

Use initialization for the starting condition

execution.initialize defines what must exist when the agreement begins.
{
  "initialize": {
    "initialState": "AWAITING_PAYMENT",
    "data": {
      "serviceProviderRepresentative": "${variables.serviceProviderRepresentative}",
      "clientRepresentative": "${variables.clientRepresentative}",
      "retainerTitle": "${variables.retainerTitle}"
    }
  }
}
Facts required at deployment belong in initialization. Facts produced later by real-world events belong in inputs.

Authoring checklist

Before validation, confirm that:
  1. the agreement models a real workflow
  2. each variable has a clear purpose
  3. participant roles are explicit
  4. agreement text uses the same vocabulary as the model
  5. states and inputs describe real lifecycle moments
  6. issuers match responsibility
  7. transitions reflect the intended process
  8. initialization includes the values required at the start

Next step

Use Validate Agreement Structure to check participant roles, input IDs, state IDs, and warnings before deployment preflight.