> 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/api-rest/deploy/initialize-escrow.md).

# Deploy

Builds the unsigned transaction that deploys a new **single-release v2** escrow through the Trustless Work factory. You sign it with the `signer` wallet and submit it via [Send Transaction](/trustless-work/v2-en/api-rest/stellar/send-transaction.md) — the API never holds keys.

{% hint style="info" %}
The request and response reference below is generated from the **live API spec** and always matches what the server actually accepts. If anything on this page ever disagrees with it, the reference wins.
{% endhint %}

## Authentication

Send your API key on every call: `x-api-key: <id>.<secret>` (or a wallet-session `Authorization: Bearer <jwt>`). Body is JSON (`Content-Type: application/json`).

## Endpoint

## Build the unsigned transaction to deploy a new v2 escrow

> Returns the XDR that you sign with the \`signer\` wallet. v2 differs from v1 only in that \`roles\` accepts an optional \`observers\` list.

```json
{"openapi":"3.0.0","info":{"title":"👋🏼 Trustless Work Core API — All Endpoints (Unified)","version":"1.0"},"security":[{}],"paths":{"/escrow/single-release/v2/deploy":{"post":{"description":"Returns the XDR that you sign with the `signer` wallet. v2 differs from v1 only in that `roles` accepts an optional `observers` list.","operationId":"DeployEscrowV2Controller_deploy","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployEscrowV2Dto"}}}},"responses":{"200":{"description":"Unsigned XDR built successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnsignedTransactionResponse"}}}},"400":{"description":"Request body or query params fail schema validation (wrong type, missing required field, unknown property).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsResponseDto"}}}},"401":{"description":"Missing, malformed, expired, revoked or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsResponseDto"}}}},"403":{"description":"API key is valid but does not have the role required for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsResponseDto"}}}},"422":{"description":"Payload is well-formed but a business rule rejects it (e.g. amount out of range, role not allowed for this user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsResponseDto"}}}},"500":{"description":"Unexpected server-side failure. Use the `traceId` to look up the incident in server logs or contact support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetailsResponseDto"}}}}},"summary":"Build the unsigned transaction to deploy a new v2 escrow","tags":["Single Release Escrow V2"]}}},"components":{"schemas":{"DeployEscrowV2Dto":{"type":"object","properties":{"signer":{"type":"string","description":"Wallet that will sign the deploy transaction."},"engagementId":{"type":"string","description":"Stable identifier the platform uses to correlate the escrow."},"title":{"type":"string","description":"Short human-readable title for the escrow."},"description":{"type":"string","description":"Long-form description."},"roles":{"$ref":"#/components/schemas/RolesV2Dto"},"amount":{"type":"number","description":"Total escrow amount in human-readable decimals."},"platformFee":{"type":"number","description":"Platform fee in percent (1 = 1%). Scaled to basis points on-chain."},"milestones":{"description":"Milestones to register at deploy time. Optional — the contract accepts deploys with an empty (or absent) milestones array since contract commit `b8738b9`. Use `POST /escrow/single-release/v2/manage-milestones` afterwards to add them. Max 50 entries when present.","type":"array","items":{"$ref":"#/components/schemas/MilestoneV2Dto"}},"trustline":{"$ref":"#/components/schemas/TrustlineV2Dto"},"receiverMemo":{"type":"number","description":"Optional receiver memo (u32 on-chain)."}},"required":["signer","engagementId","title","description","roles","amount","platformFee","trustline"]},"RolesV2Dto":{"type":"object","properties":{"approvers":{"description":"Wallets allowed to approve milestones. Up to 5, no duplicates.","type":"array","items":{"type":"string"}},"serviceProviders":{"description":"Wallets that perform the work and change milestone status. Up to 5.","type":"array","items":{"type":"string"}},"platform":{"type":"string","description":"Trustless Work platform wallet. Receives the platform fee."},"releaseSigners":{"description":"Wallets authorised to release the funds once milestones are met. Up to 5, no duplicates.","type":"array","items":{"type":"string"}},"disputeResolvers":{"description":"Wallets that resolve disputes when they are raised. Up to 5, no duplicates. Cannot overlap with any other role.","type":"array","items":{"type":"string"}},"receiver":{"type":"string","description":"Final beneficiary of the released funds."},"admin":{"type":"string","description":"Admin wallet — authorises update_escrow, manage_milestones and extend_contract_ttl. MUST be distinct from every other role."},"observers":{"description":"Read-only observer wallets attached to the escrow. They have no on-chain authority. Defaults to an empty list when absent.","type":"array","items":{"type":"string"}}},"required":["approvers","serviceProviders","platform","releaseSigners","disputeResolvers","receiver","admin"]},"MilestoneV2Dto":{"type":"object","properties":{"description":{"type":"string","description":"Human-readable milestone description."},"status":{"type":"string","description":"Initial status. Convention: pending → in_progress → completed. Defaults to \"pending\"."},"approvalsTarget":{"type":"number","description":"Number of distinct approvers required for the milestone. Must be > 0 and ≤ roles.approvers.length. Defaults to 1."}},"required":["description"]},"TrustlineV2Dto":{"type":"object","properties":{"contractId":{"type":"string","description":"Soroban contract address of the asset (C...). Required if symbol+address are not provided."},"symbol":{"type":"string","description":"Asset code / symbol. Required if contractId is not provided; ignored otherwise."},"address":{"type":"string","description":"Issuer account address (G...). Required if contractId is not provided; ignored otherwise."}}},"UnsignedTransactionResponse":{"type":"object","properties":{"unsignedXdr":{"type":"string","description":"Base64-encoded unsigned XDR. Sign it with the appropriate wallet and submit to Soroban / Horizon to execute the operation on-chain."},"txHash":{"type":"string","description":"SHA-256 hash of the prepared transaction. NOTE: it only matches the executed transaction if you sign the XDR exactly as returned — re-preparing (re-simulating) it changes the hash. Prefer `contractId` for correlation when present."},"contractId":{"type":"object","description":"Deploys only: the address (C…) the new escrow contract WILL have once this transaction executes — known upfront because the service generates the deploy salt. Track your escrow with it (e.g. `GET /escrows/:contractId`) as soon as the transaction lands; it is stable even if your wallet/SDK re-prepares the transaction.","nullable":true}},"required":["unsignedXdr","txHash"]},"ProblemDetailsResponseDto":{"type":"object","properties":{"type":{"type":"string","description":"Stable URI identifying the error type. Hash-link points to the docs."},"title":{"type":"string","description":"Short human-readable summary of the problem."},"status":{"type":"number","description":"HTTP status code, repeated here for typed clients."},"code":{"type":"string","description":"Machine-readable error code. Stable across versions; safe to switch on."},"detail":{"type":"string","description":"Human-readable explanation specific to this occurrence."},"instance":{"type":"string","description":"URI of the request that triggered the error."},"traceId":{"type":"string","description":"Correlation id propagated from the `X-Trace-Id` header (or generated). Use it when contacting support to reference a specific request."},"extensions":{"type":"object","description":"Optional structured context. Shape depends on the error code; documented per-endpoint when relevant.","additionalProperties":true}},"required":["type","title","status","code","detail"]}}}}
```

## Field notes

Cross-field rules the schema alone cannot express:

* `roles.admin` must be **distinct from every other role**. It is the wallet that later signs `update-escrow`, `manage-milestones` and `extend-ttl`.
* `milestones` is **optional at deploy** — you can deploy with none and add them later with [Manage Milestones](/trustless-work/v2-en/api-rest/deploy/manage-milestones.md). Each milestone's `approvalsTarget` must be ≤ the number of `roles.approvers`.
* `trustline` accepts **either** the token's `contractId` (`C…`) **or** the classic `symbol` + `address` pair.
* `amount` is in human token units (e.g. `100.50` USDC); `platformFee` is a percent (`1` = 1%).

## What this endpoint returns

* `unsignedXdr` — sign it **exactly as returned**. Re-preparing (re-simulating) it in your wallet changes the transaction hash.
* `txHash` — hash of the prepared transaction. Only matches the executed one if you did not re-prepare.
* `contractId` — the address (`C…`) the escrow **will** have once the transaction lands. It is known upfront and stable, so use it to track the escrow: [`GET /escrows/{contractId}`](/trustless-work/v2-en/api-rest/escrows/get-escrow.md).

## Usage example

```typescript
import axios from "axios";

const http = axios.create({
  baseURL: "https://beta.api.trustlesswork.com",
  timeout: 10000,
  headers: {
    "Content-Type": "application/json",
    "x-api-key": process.env.TW_API_KEY, // format: id.secret
  },
});

export const deployEscrow = async () => {
  // 1. BUILD — the API returns the unsigned XDR and the future contract id
  const { data: build } = await http.post(
    "/escrow/single-release/v2/deploy",
    {
      // deploy payload — see the endpoint reference above
    },
  );
  const { unsignedXdr, contractId } = build;

  // 2. SIGN — locally, with the wallet that owns `signer`
  const { address } = await kit.getAddress();
  const { signedTxXdr } = await signTransaction(unsignedXdr, {
    address,
    networkPassphrase: WalletNetwork.TESTNET,
  });

  // 3. SUBMIT
  const { data: result } = await http.post("/stellar/send-transaction", {
    signedXdr: signedTxXdr,
  });

  // On success `result` carries the freshly indexed escrow, or
  // code STELLAR_TX_SUBMITTED_INDEXER_LAGGING if the read-model
  // is still catching up — poll GET /escrows/{contractId} briefly.
  return { contractId, result };
};
```

## Next steps

* Submit the signed XDR: [Send Transaction](/trustless-work/v2-en/api-rest/stellar/send-transaction.md)
* Lock the funds: [Fund Escrow](/trustless-work/v2-en/api-rest/deploy/fund-escrow.md)
* Read it back: [`GET /escrows/{contractId}`](/trustless-work/v2-en/api-rest/escrows/get-escrow.md)


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.trustlesswork.com/trustless-work/v2-en/api-rest/deploy/initialize-escrow.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
