> For the complete documentation index, see [llms.txt](https://docs.trustlesswork.com/trustless-work/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.trustlesswork.com/trustless-work/v2-en/introduction/developer-resources/schema.md).

# Schema

Technical reference for the Trustless Work V2 Single-Release and Multi-Release escrow objects returned by Core API v2.

## Schema

Trustless Work V2 supports two escrow families:

* **Single Release**
* **Multi Release**

They share the same core concepts but place settlement state differently.

This page documents the **Core API v2 read model** returned to applications.

It is not a literal copy of the Soroban storage structs.

The API normalizes on-chain data into application-friendly camelCase objects and enriches some fields such as asset metadata.

***

## Core differences

The most important structural distinction is:

| Property       | Single Release    | Multi Release                 |
| -------------- | ----------------- | ----------------------------- |
| Amount         | Escrow-level      | Per milestone                 |
| Receiver       | Escrow-level role | Per milestone                 |
| Dispute        | Escrow-level      | Per milestone                 |
| Released state | Escrow-level      | Per milestone                 |
| Approval       | Per milestone     | Per milestone                 |
| Settlement     | One final payout  | Independent milestone payouts |

Everything else follows from that distinction.

[Compare Escrow Types](https://docs.trustlesswork.com/trustless-work/v2-en/introduction/technology-overview/escrow-types)

***

## Single-Release Escrow

A Single-Release Escrow represents multiple conditions leading to one final settlement.

The Core API read shape is conceptually:

```json
{
  "type": "single-release",
  "contractId": "C...",
  "contractBaseId": "C...",
  "engagementId": "ENG-001",
  "title": "Website Development",
  "description": "Milestone-based development agreement",
  "amount": 10000,
  "balance": 10000,
  "platformFee": 1,
  "receiverMemo": 0,
  "trustline": {
    "address": "C...",
    "contractId": "C...",
    "symbol": "USDC"
  },
  "roles": {
    "approvers": ["G..."],
    "serviceProviders": ["G..."],
    "platform": "G...",
    "releaseSigners": ["G..."],
    "disputeResolvers": ["G..."],
    "receiver": "G...",
    "admin": "G...",
    "observers": []
  },
  "milestones": [
    {
      "description": "Production deployment",
      "status": "Ready for Review",
      "evidence": "https://example.com/evidence",
      "approvals": {
        "target": 2,
        "approvalCount": 1,
        "approvedBy": ["G..."]
      }
    }
  ],
  "dispute": {
    "isDisputed": false,
    "reason": "",
    "resolved": false
  },
  "released": false
}
```

***

### Single-Release fields

| Field            | Type                 | Description                                                           |
| ---------------- | -------------------- | --------------------------------------------------------------------- |
| `type`           | `string`             | Escrow family. For this object: `single-release`.                     |
| `contractId`     | `string`             | Deployed escrow contract identifier.                                  |
| `contractBaseId` | `string`             | Base/factory contract associated with this escrow deployment.         |
| `engagementId`   | `string`             | External business identifier supplied by the integrator.              |
| `title`          | `string`             | Human-readable escrow title.                                          |
| `description`    | `string`             | Human-readable escrow description.                                    |
| `amount`         | `number`             | Total Single-Release settlement amount in human-readable token units. |
| `balance`        | `number`             | Current funded token balance.                                         |
| `platformFee`    | `number`             | Platform fee normalized by Core API as a percentage.                  |
| `receiverMemo`   | `number`             | Numeric receiver memo stored with the escrow.                         |
| `trustline`      | `Trustline`          | Asset metadata used by the escrow.                                    |
| `roles`          | `SingleReleaseRoles` | Role assignments.                                                     |
| `milestones`     | `Milestone[]`        | Conditions that gate final settlement.                                |
| `dispute`        | `Dispute`            | Dispute state for the entire escrow.                                  |
| `released`       | `boolean`            | Whether the Single-Release settlement has already executed.           |

***

## Single-Release Roles

```json
{
  "approvers": ["G..."],
  "serviceProviders": ["G..."],
  "platform": "G...",
  "releaseSigners": ["G..."],
  "disputeResolvers": ["G..."],
  "receiver": "G...",
  "admin": "G...",
  "observers": ["G..."]
}
```

| Field              | Type       | Description                                                   |
| ------------------ | ---------- | ------------------------------------------------------------- |
| `approvers`        | `string[]` | Addresses authorized to approve milestones.                   |
| `serviceProviders` | `string[]` | Addresses authorized to update milestone status and evidence. |
| `platform`         | `string`   | Platform address associated with the escrow and Platform Fee. |
| `releaseSigners`   | `string[]` | Addresses authorized to execute eligible settlement.          |
| `disputeResolvers` | `string[]` | Addresses authorized to resolve existing disputes.            |
| `receiver`         | `string`   | Final beneficiary of the Single-Release payout.               |
| `admin`            | `string`   | Address with supported configuration authority.               |
| `observers`        | `string[]` | Read-only participant addresses.                              |

Operational role lists support multiple addresses.

V2 caps role lists at **5 addresses**.

The Receiver, Platform, and Admin are singular fields.

[Review Roles and Permissions](https://docs.trustlesswork.com/trustless-work/v2-en/introduction/technology-overview/roles-in-trustless-work)

***

## Single-Release Milestone

```json
{
  "description": "Production deployment",
  "status": "Ready for Review",
  "evidence": "https://example.com/release",
  "approvals": {
    "target": 2,
    "approvalCount": 1,
    "approvedBy": ["G..."]
  }
}
```

| Field         | Type                 | Description                                            |
| ------------- | -------------------- | ------------------------------------------------------ |
| `description` | `string`             | Description of the milestone condition or deliverable. |
| `status`      | `string`             | Application-defined progress value.                    |
| `evidence`    | `string`             | Evidence or reference associated with the milestone.   |
| `approvals`   | `MilestoneApprovals` | Threshold approval state.                              |

A Single-Release milestone does **not** contain:

* amount
* Receiver
* dispute state
* released state

Those economic properties belong to the escrow as a whole.

***

## Milestone Approvals

V2 replaces the V1 `approved: boolean` model with threshold approvals.

```json
{
  "target": 2,
  "approvalCount": 1,
  "approvedBy": [
    "G..."
  ]
}
```

| Field           | Type       | Description                                         |
| --------------- | ---------- | --------------------------------------------------- |
| `target`        | `number`   | Number of distinct approvals required.              |
| `approvalCount` | `number`   | Number of approvals currently recorded.             |
| `approvedBy`    | `string[]` | Addresses that have already approved the milestone. |

A milestone satisfies its approval requirement when:

```
approvalCount >= target
```

Do not derive protocol approval from the milestone's `status` string.

For example:

```json
{
  "status": "Approved",
  "approvals": {
    "target": 2,
    "approvalCount": 0,
    "approvedBy": []
  }
}
```

is **not approved at the protocol level**.

[Review the Approval Phase](https://docs.trustlesswork.com/trustless-work/v2-en/introduction/technology-overview/escrow-lifecycle/approval-phase)

***

## Dispute

```json
{
  "isDisputed": true,
  "reason": "Deliverable does not satisfy the agreed acceptance criteria.",
  "resolved": false
}
```

| Field        | Type      | Description                                   |
| ------------ | --------- | --------------------------------------------- |
| `isDisputed` | `boolean` | Whether the dispute is currently open.        |
| `reason`     | `string`  | Reason supplied when the dispute was opened.  |
| `resolved`   | `boolean` | Whether the dispute has reached a resolution. |

For Single Release, this object belongs to the escrow.

For Multi Release, each milestone contains its own Dispute object.

***

## Multi-Release Escrow

A Multi-Release Escrow represents independent milestone settlements.

The Core API read shape is conceptually:

```json
{
  "type": "multi-release",
  "contractId": "C...",
  "contractBaseId": "C...",
  "engagementId": "ENG-002",
  "title": "Grant Program",
  "description": "Three-stage grant agreement",
  "balance": 6000,
  "platformFee": 1,
  "receiverMemo": 0,
  "trustline": {
    "address": "C...",
    "contractId": "C...",
    "symbol": "USDC"
  },
  "roles": {
    "approvers": ["G...", "G..."],
    "serviceProviders": ["G..."],
    "platform": "G...",
    "releaseSigners": ["G..."],
    "disputeResolvers": ["G..."],
    "admin": "G...",
    "observers": []
  },
  "milestones": [
    {
      "description": "Prototype",
      "status": "Ready for Review",
      "evidence": "https://example.com/prototype",
      "approvals": {
        "target": 2,
        "approvalCount": 2,
        "approvedBy": ["G...", "G..."]
      },
      "amount": 2000,
      "dispute": {
        "isDisputed": false,
        "reason": "",
        "resolved": false
      },
      "released": false,
      "receiver": "G..."
    }
  ]
}
```

***

### Multi-Release fields

| Field            | Type                      | Description                                                   |
| ---------------- | ------------------------- | ------------------------------------------------------------- |
| `type`           | `string`                  | Escrow family. For this object: `multi-release`.              |
| `contractId`     | `string`                  | Deployed escrow contract identifier.                          |
| `contractBaseId` | `string`                  | Base/factory contract associated with this escrow deployment. |
| `engagementId`   | `string`                  | External business identifier supplied by the integrator.      |
| `title`          | `string`                  | Human-readable escrow title.                                  |
| `description`    | `string`                  | Human-readable escrow description.                            |
| `balance`        | `number`                  | Current funded token balance.                                 |
| `platformFee`    | `number`                  | Platform fee normalized by Core API as a percentage.          |
| `receiverMemo`   | `number`                  | Numeric receiver memo stored with the escrow.                 |
| `trustline`      | `Trustline`               | Asset metadata used by the escrow.                            |
| `roles`          | `MultiReleaseRoles`       | Global role assignments.                                      |
| `milestones`     | `MultiReleaseMilestone[]` | Independently settleable milestones.                          |

Multi Release does **not** contain top-level:

* `amount`
* `receiver`
* `dispute`
* `released`

Those properties belong to individual milestones.

***

## Multi-Release Roles

```json
{
  "approvers": ["G..."],
  "serviceProviders": ["G..."],
  "platform": "G...",
  "releaseSigners": ["G..."],
  "disputeResolvers": ["G..."],
  "admin": "G...",
  "observers": ["G..."]
}
```

The role model is the same as Single Release except there is no escrow-level `receiver`.

| Field              | Type       | Description                                                     |
| ------------------ | ---------- | --------------------------------------------------------------- |
| `approvers`        | `string[]` | Global Approvers for escrow milestones.                         |
| `serviceProviders` | `string[]` | Addresses authorized to update milestone status and evidence.   |
| `platform`         | `string`   | Platform address associated with the escrow and Platform Fee.   |
| `releaseSigners`   | `string[]` | Addresses authorized to execute eligible milestone settlements. |
| `disputeResolvers` | `string[]` | Addresses authorized to resolve disputes.                       |
| `admin`            | `string`   | Address with supported configuration authority.                 |
| `observers`        | `string[]` | Read-only participant addresses.                                |

Receivers live directly on each milestone.

***

## Multi-Release Milestone

```json
{
  "description": "Security Audit",
  "status": "Ready for Review",
  "evidence": "https://example.com/audit",
  "approvals": {
    "target": 2,
    "approvalCount": 2,
    "approvedBy": ["G...", "G..."]
  },
  "amount": 3000,
  "dispute": {
    "isDisputed": false,
    "reason": "",
    "resolved": false
  },
  "released": false,
  "receiver": "G..."
}
```

| Field         | Type                 | Description                                          |
| ------------- | -------------------- | ---------------------------------------------------- |
| `description` | `string`             | Milestone condition or deliverable.                  |
| `status`      | `string`             | Application-defined progress value.                  |
| `evidence`    | `string`             | Evidence or external reference.                      |
| `approvals`   | `MilestoneApprovals` | Threshold approval state for this milestone.         |
| `amount`      | `number`             | Settlement amount allocated to this milestone.       |
| `dispute`     | `Dispute`            | Dispute state for this milestone.                    |
| `released`    | `boolean`            | Whether this milestone has already settled normally. |
| `receiver`    | `string`             | Final beneficiary of this milestone's settlement.    |

Each milestone can therefore progress independently.

For example:

```
Milestone 1 → released

Milestone 2 → disputed

Milestone 3 → waiting for approval
```

***

## Trustline

The Core API read model exposes asset information as:

```json
{
  "address": "C...",
  "contractId": "C...",
  "symbol": "USDC"
}
```

| Field        | Type     | Description                                                          |
| ------------ | -------- | -------------------------------------------------------------------- |
| `address`    | `string` | Token contract address used by the escrow.                           |
| `contractId` | `string` | Contract identifier exposed by the API for the same asset reference. |
| `symbol`     | `string` | Token symbol resolved by the Core API, such as `USDC`.               |

There is an important implementation distinction here.

The raw V2 Soroban contract stores:

```rust
pub struct Trustline {
    pub address: Address,
}
```

The additional `contractId` and `symbol` fields are part of the **Core API representation**, not additional fields stored in the escrow's V2 `Trustline` struct.

This is why API schema and contract storage should not be treated as identical representations.

***

## API names vs on-chain names

Soroban structs use Rust-style snake\_case.

For example:

```
service_providers
release_signers
dispute_resolvers
approval_count
approved_by
is_disputed
platform_fee
receiver_memo
```

Core API v2 exposes application-friendly camelCase:

```
serviceProviders
releaseSigners
disputeResolvers
approvalCount
approvedBy
isDisputed
platformFee
receiverMemo
```

Use the schema of the integration surface you are calling.

Do not copy raw contract field names into API requests or responses unless that endpoint explicitly expects them.

***

## Amount normalization

Raw Soroban token amounts are integer values using the token's decimal precision.

Core API v2 normalizes these values before returning them.

For example:

```
Raw contract value
100000000

Token decimals
7

Core API value
10
```

Current V2 HTTP read DTOs expose normalized amounts as JSON numbers.

This applies to values such as:

* `amount`
* `balance`
* milestone `amount`

Applications should use the Core API representation rather than manually interpreting raw contract integers when consuming API reads.

***

## Platform Fee representation

The V2 smart contract stores the Platform Fee internally as a basis-point value.

Core API v2 converts that raw value into a percentage for the HTTP read model.

Conceptually:

```
Contract:
100 basis points

Core API:
1
```

meaning:

```
1%
```

Do not assume the contract's raw fee integer and the HTTP API's `platformFee` number use the same unit.

***

## Status is not protocol state

The following values are separate:

```
status
approvals
dispute
released
```

`status` is application-defined text.

For example:

```json
{
  "status": "Completed"
}
```

does not mean:

```json
{
  "approvals": {
    "target": 2,
    "approvalCount": 2
  }
}
```

Likewise, using:

```
"In Dispute"
```

as a status string does not set:

```json
{
  "dispute": {
    "isDisputed": true
  }
}
```

Applications must use the dedicated protocol fields for economic authorization.

> **Status ≠ Approval ≠ Dispute ≠ Release**

***

## Evidence

The read model exposes milestone evidence as a string:

```json
{
  "evidence": "https://example.com/proof"
}
```

When updating milestone status, V2 allows the new evidence value to be omitted.

If no new evidence is supplied, the existing evidence remains unchanged.

That update behavior should not be confused with the read schema, where `evidence` is returned as the milestone's current string value.

***

## Contract storage vs Core API read model

Do not assume these layers expose identical objects.

```
Soroban contract
↓
Raw on-chain structs
↓
Core API parser
↓
Normalized domain model
↓
HTTP response mapper
↓
Application-facing JSON
```

Core API can:

* convert snake\_case to camelCase
* normalize token amounts
* normalize Platform Fee units
* add asset symbols
* add deployment metadata such as `contractId`
* omit internal or redundant fields from specific responses

The smart contract remains authoritative for execution.

The API schema is the correct interface for applications consuming Core API v2.

***

## Endpoint-specific response shapes

This page describes the primary **V2 escrow read object**.

Individual endpoints may wrap or adapt that object.

For example, transaction-submission responses can include:

* transaction hash
* ledger information
* deployed contract ID
* an escrow snapshot

Some redundant or internal fields may be omitted from those snapshots.

Always use the endpoint's API reference as the final source for that endpoint's exact response envelope.

[Explore the V2 REST API](https://docs.trustlesswork.com/trustless-work/v2-en/api-rest)

***

## Single vs Multi reference

| Property             | Single Release   | Multi Release   |
| -------------------- | ---------------- | --------------- |
| `type`               | `single-release` | `multi-release` |
| `contractId`         | Escrow-level     | Escrow-level    |
| `balance`            | Escrow-level     | Escrow-level    |
| `platformFee`        | Escrow-level     | Escrow-level    |
| `roles.receiver`     | Yes              | No              |
| `amount`             | Escrow-level     | Per milestone   |
| `milestone.receiver` | No               | Yes             |
| `dispute`            | Escrow-level     | Per milestone   |
| `released`           | Escrow-level     | Per milestone   |
| `approvals`          | Per milestone    | Per milestone   |

***

### Related concepts

[Escrow Properties](https://docs.trustlesswork.com/trustless-work/v2-en/introduction/technology-overview/what-does-a-smart-escrow-look-like)

[Escrow Types](https://docs.trustlesswork.com/trustless-work/v2-en/introduction/technology-overview/escrow-types)

[Roles and Permissions](https://docs.trustlesswork.com/trustless-work/v2-en/introduction/technology-overview/roles-in-trustless-work)

[Escrow Lifecycle](https://docs.trustlesswork.com/trustless-work/v2-en/introduction/technology-overview/escrow-lifecycle)

[Developer Guide](https://docs.trustlesswork.com/trustless-work/v2-en/introduction/developer-resources)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.trustlesswork.com/trustless-work/v2-en/introduction/developer-resources/schema.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
