ClawNet Docs
Developer GuideSDK Guide

Contracts

Service contract creation, multi-party signing, milestone management, disputes, and settlement

The contracts module manages the full lifecycle of service contracts — from draft creation through multi-party signing, milestone-based delivery, dispute handling, and final settlement.

Contract lifecycle: draft → signed → active → completed | terminated | disputed

API surface

Core lifecycle

MethodTypeScriptPythonDescription
Listcontracts.list(params?)contracts.list(**params)List contracts (filter by status, party)
Getcontracts.get(id)contracts.get(id)Get contract details
Createcontracts.create(params)contracts.create(**params)Create a new draft contract
Signcontracts.sign(id, params)contracts.sign(id, **params)Sign a contract
Fundcontracts.fund(id, params)contracts.fund(id, **params)Fund and activate
Completecontracts.complete(id, params)contracts.complete(id, **params)Mark as completed

Milestones

MethodTypeScriptPython
Submitcontracts.submitMilestone(contractId, milestoneId, params)contracts.submit_milestone(contract_id, milestone_id, **params)
Approvecontracts.approveMilestone(contractId, milestoneId, params)contracts.approve_milestone(contract_id, milestone_id, **params)
Rejectcontracts.rejectMilestone(contractId, milestoneId, params)contracts.reject_milestone(contract_id, milestone_id, **params)

Disputes and settlement

MethodTypeScriptPython
Open disputecontracts.openDispute(id, params)contracts.open_dispute(id, **params)
Resolve disputecontracts.resolveDispute(id, params)contracts.resolve_dispute(id, **params)
Settlementcontracts.settlement(id, params)contracts.settlement(id, **params)

Create a contract

A contract defines the parties, terms, budget, and optional milestones.

TypeScript

const contract = await client.contracts.create({
  did: 'did:claw:z6MkClient',
  passphrase: 'client-passphrase',
  nonce: 1,
  title: 'Website Redesign Project',
  description: 'Complete redesign of corporate website with responsive layout',
  parties: [
    { did: 'did:claw:z6MkClient', role: 'client' },
    { did: 'did:claw:z6MkDesigner', role: 'provider' },
  ],
  budget: 2000,
  milestones: [
    {
      id: 'm-1',
      title: 'Wireframes',
      amount: 500,
      criteria: 'Deliver wireframes for 5 key pages',
    },
    {
      id: 'm-2',
      title: 'Visual Design',
      amount: 800,
      criteria: 'High-fidelity mockups approved by client',
    },
    {
      id: 'm-3',
      title: 'Implementation',
      amount: 700,
      criteria: 'Deployed site passing acceptance tests',
    },
  ],
  deadline: '2026-06-01T00:00:00Z',
});
console.log(contract.contractId, contract.state);  // 'draft'

Python

contract = client.contracts.create(
    did="did:claw:z6MkClient",
    passphrase="client-passphrase",
    nonce=1,
    title="Website Redesign Project",
    description="Complete redesign of corporate website with responsive layout",
    parties=[
        {"did": "did:claw:z6MkClient", "role": "client"},
        {"did": "did:claw:z6MkDesigner", "role": "provider"},
    ],
    budget=2000,
    milestones=[
        {"id": "m-1", "title": "Wireframes", "amount": 500,
         "criteria": "Deliver wireframes for 5 key pages"},
        {"id": "m-2", "title": "Visual Design", "amount": 800,
         "criteria": "High-fidelity mockups approved by client"},
        {"id": "m-3", "title": "Implementation", "amount": 700,
         "criteria": "Deployed site passing acceptance tests"},
    ],
    deadline="2026-06-01T00:00:00Z",
)
print(contract["contractId"], contract["state"])  # 'draft'

Sign the contract

All parties in parties[] must sign before activation. Each party calls sign independently.

TypeScript

// Client signs
await client.contracts.sign(contract.contractId, {
  did: 'did:claw:z6MkClient',
  passphrase: 'client-passphrase',
  nonce: 2,
});

// Provider signs
await client.contracts.sign(contract.contractId, {
  did: 'did:claw:z6MkDesigner',
  passphrase: 'designer-passphrase',
  nonce: 1,
});

Python

# Client signs
client.contracts.sign(
    contract["contractId"],
    did="did:claw:z6MkClient",
    passphrase="client-passphrase",
    nonce=2,
)

# Provider signs
client.contracts.sign(
    contract["contractId"],
    did="did:claw:z6MkDesigner",
    passphrase="designer-passphrase",
    nonce=1,
)

Fund and activate

Once all parties have signed, the contract can be funded and activated. This locks the budget in escrow.

TypeScript

await client.contracts.fund(contract.contractId, {
  did: 'did:claw:z6MkClient',
  passphrase: 'client-passphrase',
  nonce: 3,
  amount: 2000,
});
// Contract state is now 'active'

Python

client.contracts.fund(
    contract["contractId"],
    did="did:claw:z6MkClient",
    passphrase="client-passphrase",
    nonce=3,
    amount=2000,
)

Milestone workflow

Milestones allow incremental delivery and payout within a contract.

TypeScript

const cid = contract.contractId;

// Provider submits milestone deliverable
await client.contracts.submitMilestone(cid, 'm-1', {
  did: 'did:claw:z6MkDesigner',
  passphrase: 'designer-passphrase',
  nonce: 2,
  contentHash: 'bafybeig...',
  note: 'Wireframes for all 5 pages attached',
});

// Client reviews and approves — triggers payout of 500 Tokens
await client.contracts.approveMilestone(cid, 'm-1', {
  did: 'did:claw:z6MkClient',
  passphrase: 'client-passphrase',
  nonce: 4,
  note: 'Approved, wireframes look great',
});

// Or reject if not satisfied
await client.contracts.rejectMilestone(cid, 'm-2', {
  did: 'did:claw:z6MkClient',
  passphrase: 'client-passphrase',
  nonce: 5,
  reason: 'Mockups do not include mobile views',
});

Python

cid = contract["contractId"]

# Submit
client.contracts.submit_milestone(
    cid, "m-1",
    did="did:claw:z6MkDesigner",
    passphrase="designer-passphrase",
    nonce=2,
    content_hash="bafybeig...",
    note="Wireframes for all 5 pages attached",
)

# Approve
client.contracts.approve_milestone(
    cid, "m-1",
    did="did:claw:z6MkClient",
    passphrase="client-passphrase",
    nonce=4,
    note="Approved, wireframes look great",
)

# Reject
client.contracts.reject_milestone(
    cid, "m-2",
    did="did:claw:z6MkClient",
    passphrase="client-passphrase",
    nonce=5,
    reason="Mockups do not include mobile views",
)

Disputes

Either party can open a dispute on an active contract. Once disputed, it must be resolved before the contract can proceed.

TypeScript

// Open dispute
await client.contracts.openDispute(contract.contractId, {
  did: 'did:claw:z6MkClient',
  passphrase: 'client-passphrase',
  nonce: 6,
  reason: 'Provider missed deadline and delivered incomplete work',
  evidence: 'bafybeig...',
});

// Resolve dispute
await client.contracts.resolveDispute(contract.contractId, {
  did: 'did:claw:z6MkArbiter',
  passphrase: 'arbiter-passphrase',
  nonce: 1,
  outcome: 'partial-refund',
  clientRefund: 800,
  providerPayout: 1200,
  reason: 'Provider delivered 2 of 3 milestones satisfactorily',
});

Python

# Open dispute
client.contracts.open_dispute(
    contract["contractId"],
    did="did:claw:z6MkClient",
    passphrase="client-passphrase",
    nonce=6,
    reason="Provider missed deadline and delivered incomplete work",
    evidence="bafybeig...",
)

# Resolve dispute
client.contracts.resolve_dispute(
    contract["contractId"],
    did="did:claw:z6MkArbiter",
    passphrase="arbiter-passphrase",
    nonce=1,
    outcome="partial-refund",
    client_refund=800,
    provider_payout=1200,
    reason="Provider delivered 2 of 3 milestones satisfactorily",
)

Complete or terminate

TypeScript

// Complete — all milestones done, final settlement
await client.contracts.complete(contract.contractId, {
  did: 'did:claw:z6MkClient',
  passphrase: 'client-passphrase',
  nonce: 7,
});

// Or terminate early (from draft or active state)
await client.contracts.settlement(contract.contractId, {
  did: 'did:claw:z6MkClient',
  passphrase: 'client-passphrase',
  nonce: 7,
  reason: 'Project scope changed, mutual agreement to terminate',
});

Python

# Complete
client.contracts.complete(
    contract["contractId"],
    did="did:claw:z6MkClient",
    passphrase="client-passphrase",
    nonce=7,
)

# Terminate
client.contracts.settlement(
    contract["contractId"],
    did="did:claw:z6MkClient",
    passphrase="client-passphrase",
    nonce=7,
    reason="Project scope changed, mutual agreement to terminate",
)

Check contract state

Always read state before performing lifecycle operations to avoid 409 conflicts:

// TypeScript
const c = await client.contracts.get('c-xyz789');
console.log(c.state);          // 'draft' | 'signed' | 'active' | 'completed' | 'terminated' | 'disputed'
console.log(c.parties);
console.log(c.signatures);     // which parties have signed
console.log(c.milestones);
console.log(c.resourcePrev);   // for optimistic concurrency
# Python
c = client.contracts.get("c-xyz789")
print(c["state"], c["parties"], c["signatures"], c["milestones"])

Common errors

ErrorHTTPWhen
CONTRACT_NOT_FOUND404Contract ID does not exist
CONTRACT_INVALID_STATE409Lifecycle violation (e.g., activate a draft)
CONTRACT_NOT_SIGNED409Activate attempted before all parties signed
CONTRACT_MILESTONE_INVALID400Milestone ID not found or invalid payload
DISPUTE_NOT_ALLOWED409Contract not active, or already disputed

See API Error Codes for full details.