What you will learn
By the end of this workflow, you will have seen how:- authored agreement JSON defines variables, participants, states, inputs, issuers, and transitions
- deployment context supplies live values such as participant wallets,
chainId, and initialization data - deployment preflight normalizes the values that must be signed
- EIP-712 permits authorize deployment and input submission
- submitted inputs move an agreement through its authored state machine
- state and input history provide receipts for what happened
Before you start
This tutorial has two equivalent paths. Use the MCP path when an agent is operating Shodai through MCP tools. Use the SDK path when you are building the workflow into a TypeScript integration.| Requirement | MCP path | SDK path |
|---|---|---|
| API access | A Shodai API key configured for the hosted MCP server. | A Shodai API key passed to ApiClient. |
| Example agreement | The complex-example-agreement MCP resource. | The complete JSON from Complex Agreement. |
| Environment | environment: "testnet" for a testnet key, or environment: "production" for a production key. | new ApiClient({ environment: "testnet", apiKey }) for testnet. |
| Permit signer | An external signer that can sign returned EIP-712 typed data. | A walletClient that can sign with the intended account. |
Use testnet for a first run. Deployment and input submission are writes. They require signatures from eligible wallets, and successful submissions are not safe to retry blindly.
Run this workflow with an agent
Run this workflow with an agent
Choose a path
Both paths use the same lifecycle and the same underlying API model. The difference is the surface you operate through.| Lifecycle step | MCP tool | SDK method or helper |
|---|---|---|
| Validate authored JSON | validate_agreement | client.validateTemplate(...) |
| Preflight deployment | preflight_deployment | client.validateDeployment(...) |
| Prepare or create deploy permit | prepare_deployment_typed_data | deployAgreementWithPermit(...) or signDeployWithPermit(...) |
| Deploy with permit | deploy_agreement | deployAgreementWithPermit(...) or client.deployWithPermit(...) |
| Read current state | get_agreement_state | client.getAgreementState(...) |
| Prepare or create input permit | prepare_input_typed_data | submitAgreementInputWithPermit(...) or signAgreementInputPermit(...) |
| Submit signed input | submit_input | submitAgreementInputWithPermit(...) or client.submitAgreementInput(...) |
| Inspect input history | get_input_history | client.listAgreementInputs(...) |
Run the workflow
- MCP
- SDK
Load the service retainer example
Read the MCP resources return JSON as text. Parse the returned
complex-example-agreement MCP resource.The resource URI is:contents[0].text value and keep the parsed object as agreement for the remaining MCP tool calls.This is the same agreement documented in Complex Agreement. It starts in AWAITING_PAYMENT, moves to WORK_IN_PROGRESS after initial payment proof, supports invoice review paths, can branch into top-up review, and can terminate through a final invoice flow.Validate the authored agreement JSON
Call
validate_agreement with the API environment that matches your key and pass the parsed complex-example-agreement object as agreement.Review the validation result before continuing. The important evidence is the participant variable keys, state IDs, input IDs, and warnings. For this example, expect participant-backed variables such as serviceProviderRepresentative and clientRepresentative, and lifecycle inputs such as submitInitialPaymentProof and submitInvoice.Structural validation checks the agreement artifact only. It does not know which wallets or initialization values you will use for deployment.Prepare deployment values
Choose deployment context for the first run.
Keep participant wallet mappings and initialization values stable after preflight. If they change, regenerate the deployment typed data before signing.
| Field | Example value | Why it matters |
|---|---|---|
chainId | 59141 | Selects Linea Sepolia for a testnet deployment. |
displayName | Service Retainer Tutorial | Names the hosted agreement record. |
serviceProviderRepresentative | test wallet address | Authorizes service-provider inputs. |
clientRepresentative | test wallet address | Authorizes client inputs. |
retainerTitle | Service Retainer Tutorial | Initializes rendered agreement content. |
retainerDescription | A tutorial agreement used to learn the Shodai lifecycle. | Initializes rendered agreement content. |
serviceProviderName | Provider LLC | Initializes rendered agreement content. |
clientName | Client Inc | Initializes rendered agreement content. |
retainerCeiling | 1000 | Initializes business data used by the example. |
retainerFloor | 200 | Initializes business data used by the example. |
paymentInstructions | Record payment proof using a testnet transaction or placeholder proof URL. | Initializes rendered payment instructions. |
Preflight deployment
Call Review the returned variables, participants, observers, contributors, and warnings before any signing step.
preflight_deployment with the loaded agreement object, target chain, initialization values, and participant mappings.Use this deployment context with the loaded agreement:Preflight does not deploy the agreement. It assembles and validates the deployment request so you can sign the effective values rather than raw caller input.
Sign and deploy
For hosted MCP with external signing, call Pass the same
prepare_deployment_typed_data, sign the returned EIP-712 payload, then call deploy_agreement with the returned document link, normalized values, and permit fields.Call prepare_deployment_typed_data with:environment:"testnet"agreement: the loadedcomplex-example-agreementobjectchainId:59141signerAddress: the wallet address that will sign and own the deploymentinitValues: the same values used for preflightparticipants: the same participant mappings used for preflight
agreement, displayName, and chainId; the returned docUri and documentId when present; the normalizedInitValues, normalizedParticipants, and normalizedObservers returned by prepare_deployment_typed_data; and signer address, deadline, and signature fields into deploy_agreement.Read the deployed state
After deployment, call The service retainer starts at
get_agreement_state.AWAITING_PAYMENT. The current state tells you which authored inputs can move the lifecycle next.Submit the first lifecycle input
Submit Sign the returned EIP-712 typed data externally, then call
submitInitialPaymentProof to move the retainer from AWAITING_PAYMENT to WORK_IN_PROGRESS.First call prepare_input_typed_data with an eligible signer and the input values.submit_input with the same agreementId, inputId, values, signer address, deadline, and signature fields.The submitInitialPaymentProof input can be issued by either representative in the service retainer example. Later inputs may be restricted to one role.Try a branch
After the agreement reachesWORK_IN_PROGRESS, try one additional path to see why the complex example is useful.
Submit an invoice without a top-up request
Submit an invoice without a top-up request
Submit the This path moves the agreement to
submitInvoice input from WORK_IN_PROGRESS. The service provider representative must sign this input.Required values:INVOICE_SUBMITTED. From there, an authorized representative can approve, reject with feedback, or initiate termination.Submit an invoice with a top-up request
Submit an invoice with a top-up request
Submit the This path moves the agreement to
submitInvoiceWithTopup input from WORK_IN_PROGRESS. The service provider representative must sign this input.Required values:INVOICE_SUBMITTED_WITH_TOPUP. The next step can approve with payment proof, reject with feedback, or initiate termination.What happened under the hood
This tutorial uses one concrete agreement to exercise the core Shodai model.| Tutorial action | System concept | Where to learn more |
|---|---|---|
| Load the service retainer JSON | Agreement JSON defines the readable content and executable lifecycle. | Agreement data standard |
| Validate template | Structural validation checks authored variables, states, inputs, issuers, and transitions. | Validate Agreement Structure |
Add participant wallets and initValues | Deployment context binds the reusable agreement template to live parties and values. | Deploy an Agreement |
| Preflight before signing | Preflight normalizes effective values and catches deployment issues before authorization. | Deploy an Agreement |
| Sign deployment and input permits | EIP-712 signatures prove that an eligible wallet authorized the write. | EIP-712 Signing Reference |
Submit submitInitialPaymentProof | Inputs are signed lifecycle events accepted only from allowed issuers. | Author Agreement JSON |
| Reread state and input history | State shows the current lifecycle position; history shows the submitted event trail. | Operate a Deployed Agreement |
If a step fails
Use Errors and troubleshooting for API errors, signing failures, and lifecycle diagnostics. Before retrying a write, reread state and input history so you do not sign or submit against a stale lifecycle position.Next steps
Inspect the complex agreement
Study the complete service retainer JSON, lifecycle diagram, states, inputs, and transitions.
Author your own agreement
Turn a business workflow into agreement JSON after you have run the lifecycle once.
Build with the SDK
Use the TypeScript client reference for typed API calls, signing helpers, and diagnostics.
Quickstart with MCP
Configure the hosted MCP server and operate agreements through MCP tools.