Only this pageAll pages
Powered by GitBook
Couldn't generate the PDF for 148 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

English

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Types

All the entities that you'll need.

Overview

In this document, we explore the structure and functionality of key escrow-related types and interfaces used in the system, including the Escrow and Escrow Response entities. These entities are crucial for handling escrow operations such as initializing, updating, and responding to requests regarding escrow contracts.

Our React library includes all these types. Simply import them.

Basic

Status, baseURL and HttpMethos

/**
 * The base URL for the Trustless Work API
 */
export type baseURL =
  | "https://api.trustlesswork.com"
  | "https://dev.api.trustlesswork.com";

/**
 * Escrow Type
 */
export type EscrowType = "single-release" | "multi-release";

/**
 * Http Method
 */
export type HttpMethod = "get" | "post" | "put" | "delete";

/**
 * Unique possible statuses for a Trustless Work request
 */
export type Status = "SUCCESS" | "FAILED";

/**
 * Date
 */
export type Date = {
  _seconds: number;
  _nanoseconds: number;
};

AI optimized docs

Trustless Work documentation isn’t just written for humans. It’s structured for agents, copilots, and AI models to understand, remix, and build on top of.


Every page in our documentation is written for both:

  • Humans who want clarity, precision, and examples.

  • Machines that need structure, consistency, and semantics.


Why It Matters

The next generation of builders won’t just code — they’ll prompt. Trustless Work is optimized for that. Our docs follow an AI-ready format, meaning you can:

  • Export any page in Markdown or PDF for training datasets.

  • Feed it directly into your custom copilot or LLM memory.

  • Let your AI agents read the docs, reason through flows, and even execute API calls using your keys and roles.

🧠 Our goal: make the documentation itself a building block for automation.


Export & Train

You can export any section of this documentation into:

  • PDF – for reference and onboarding manuals

  • Markdown (.md) – for AI ingestion or context injection

  • Prompt blocks – pre-formatted snippets that can be pasted into GPT, Cursor, or v0.dev

💡 Use the SDK subpage under VibeCoding to see live examples of AI prompts generating, debugging, and deploying escrow flows automatically.


Ask the Docs

The docs themselves are AI-trained and search-optimized. That means the search bar understands natural language and intent — not just keywords.

Try prompts like:

The system will reformat your question into a structured query, returning optimized snippets, SDK calls, or workflow examples.


Links

🧭 Core Docs

  • Quickstart Guide

  • API Reference


🛠️ Tools


🌐 External Links

Developer Guide

🚀Developer QuickstartEscrow products Mix & Match GuideTrustless Work APIStellar Wallets

Dapp:

Escrow Design

We don’t hold your money—we hold the logic.

Trustless Work escrows are role-based. It is important to understand the roles to be able to correctly configure the escrows. Updates to the contract status have to be signed by addresses, and only the addresses which have a role assigned can perfom the functions that only that role can sign.


Roles

Every escrow includes a roles object. These are the available roles:

Why Escrows Matter

  • Escrows are a great way to solve for trust.

    Escrows are neutral way to hold funds while specific conditions are met. Most people tend to think about Real Estate when I mention escrows, and they are right! we DO use escrows for Real Estate and high value transactions!

But Escrows are useful for so many things, for example, marketplaces like Upwork and Ebay use them:

Ebay and Upwork can afford to build their own escrow infrastructure. Well, sort of... Ebay actually uses Escrow.com, a huge legacy escrow company that takes between (3% - 8%) , Upwork, built Upwork Escrow inc. But thats not the reality of most businesses...

Welcome

Our docs are optimized to be exported as context for any AI! You can also ask on the search bar!

Welcome to Trustless Work Documentation!

Trustless Work is the escrow infrastructure for the new economy — a developer-first platform to integrate programmable, milestone-based escrows into products.


Stellar Network

Escrows need rails. Trustless Work runs on Stellar, a blockchain optimized for stablecoins, payments, and smart contracts.

This section explains the infrastructure behind Trustless Work — the network, assets, and tools that make escrows fast, cheap, and globally accessible.


🌐 Why Stellar?

  • Fast → Transactions settle in 5 seconds

Withdraw Remaining Funds

Multi Release Only

Errors

Errors Entity

Authentication

In order to get registered, you have to connect with a Stellar wallet. If you do not yet have one, or have never used one, there is more information in the following section:

To authenticate you will need to login to our Dapp with a Stellar Wallet and Request an API key. Here are the steps to achieve that:

Coming: We are figuring out how this can work with passkeys and send to addresses with Memo. WE do this in the open, so feel free to crontribute.

Get Escrows by Contract ID

Params

Fund Escrow

Single & Multi Release

Get Balances

Params

Helpers

These endpoints provide a way to receive tokens through Trustline and send any transactions to the Stellar Blockchain.

Introduction

Helper endpoints are designed to provide additional functionalities that complement the main features of an API. They often include utility functions that streamline common requests, simplify data manipulation, or enhance overall user experience.

Trustless Work SDK

Overview

Welcome to the our React Library Reference. This documentation provides details on how to interact with our API by using our library in the simplest way.


Links

Low cost → Fractions of a cent per transaction

  • Global → Used by wallets, neobanks, fintechs, and on/off-ramp partners worldwide

  • Stablecoin-native → Home to USDC, and open to any issued asset

  • Compliance-aware → Trusted by Circle, MoneyGram, Franklin Templeton, and more


  • ⚡ Why Soroban?

    Soroban is Stellar’s smart contract engine. It powers programmable escrows — letting us define roles, milestones, and release logic directly on-chain, without building from scratch.


    📚 What You’ll Find in This Section

    • Issued Assets on Stellar Learn how USDC and other tokens work.

    • Wallets Explore supported Stellar wallets and how they sign escrow actions.

    • Testnet Tokens Get USDC/XLM on Stellar testnet to experiment with escrows before going live.

    • Libraries & Tools Discover SDKs, Wallet Kit, Soroban tools, and more for integrating escrows into your product.

    Helper Endpoint Overview

    Helper endpoints offer several benefits that enhance both development efficiency and user satisfaction.

    • Set Trustline: Allows the user to interact with some tokens.

    • Send Transaction: Send the transaction to Stellar Network with the signed hash.

    • Get Multiple Escrow Balance: This endpoint allows users to retrieve the balances of multiple escrow accounts simultaneously.

    These endpoints ensure secure transactions by leveraging Stellar's infrastructure, guaranteeing transparency and reliability.

    React SDK
    Escrow Concepts
    Templates & Examples
    Trustless Work Backoffice dApp
    Trustless Work demo dApp
    Escrow Viewer
    Swagger API Docs
    Website
    GitHub
    Only Dust Profile
    Blog: Escrow Times
    Stellar Wallets
    Request API Key
    Community & Contribution

    Who Should Use This

    Trustless Work is built for developers, platforms, and agents who want more control and automation over when funds are released.


    🧱 Platforms Using Stablecoins

    Examples:

    • Gig and freelance platforms (e.g. milestone payments)

    • B2B tools using USDC for global payouts

    • Rental and booking platforms (security deposits)

    Value:

    • Reduce fraud, automate disbursements, lower cost


    🧑‍💻 Builders & Product Teams

    Examples:

    • Solo devs or teams prototyping dApps

    • DAO tools, donation platforms, web3 marketplaces

    Value:

    • Plug-and-play escrow logic

    • Start on testnet, go live without audit headaches


    🏢 Enterprise Use

    Examples:

    • Platforms with escrow-like flows (but no infrastructure)

    • Projects looking to replace costly custodial services

    Value:

    • Save time and legal risk by using programmable, auditable escrows

    Service Provider → Can update milestone status, can raise a dispute.
  • Approver → validates milestone completion, can raise a dispute.

  • Platform Address → can make changes before escrow is funded. Is the platform fee receiver (optional configurable %fee)

  • Release Signer → executes funds release.

  • Dispute Resolver → arbitrates when things go wrong, can re-route funds if dispute is raised.

  • Receiver → final destination of funds.

  • Other roles which play no role:

    Issuer: has no powers over the contract.

    Depositor: Every incoming transaction to the escrow is indexed. But depositors play no role.

    Observer (coming in next version): Addreses that want to be observe a escrow. They play no role, but are indexed as an observer, which facilitates tracking of escrows by role.

    Escrow structure

    But Roles are only the beginning, here are more properties you should know about:

    • Escrow ID: On-chain identifier of the contract. Deposit Address. We call it like this, but it is we also reference to it as Contract Address.

    • Engagement ID → configurable string, Is meant to be used to connect the escrow with an invoice number or an external secuencer. Facilitates indexation.

    • Title → configurable string, Title of the contract.

    • Roles → who marks, approves, releases, resolves, and receives

    • Description → why the escrow exists

    • Milestones → Action that must be completed to unlock funds

    • Amount & Fees → how much is locked, how much the platform earns

    • Platform Fee → optional, how much the platform (marketplace, app, etc) earns

    • Trustline → which asset is used (USDC, or any Stellar-issued token)

    • Flags → state indicators (disputed, released, resolved)


    Two Escrow Types

    We currently support two escrow types:

    1. Single-Release Escrow Multiple milestones, one payout. Useful for deposits, one-off jobs, or simple deliveries.

    2. Multi-Release Escrow Multiple milestones, multiple payouts (one per milestone). Perfect for projects, grants, or milestone-based funding.

    More iterations are coming as we learn from your requirements! Feel free to reach out!



    Lifecycle Integration

    We constantly talk about the escrow lifecycle, which follows this path.

    1. Initiation → define schema

    2. Funding → lock assets via trustline

    3. Milestone updates → service provider adds progress

    4. Approvals → approver signs off

    5. Release → release signer triggers transfer

    6. (Optional) Dispute & Resolution


    🚀 Next Steps

    • Define Escrow Properties

    • Choose Your Escrow Type

    • Assign Roles

    • Follow the Lifecycle

    Then: Test it in our dApp Integrate Trustless Work into your platform Try out our Vibe-Coding Guide Use our escrow-blocks


    Roles in Trustless Work
    Escrow Properties
    Escrow Types
    Escrow Lifecycle
    /**
     * Withdraw remaining funds
     */
    export type WithdrawRemainingFundsPayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address in charge of resolving disputes within the escrow.
       */
      disputeResolver: string;
    
      /**
       * Distributions of the escrow amount to the receivers.
       */
      distributions: [
        {
          /**
           * Address of the receiver
           */
          address: string;
          /**
           * Amount to be transferred to the receiver. All the amount must be equal to the total amount of the escrow.
           */
          amount: number;
        },
      ];
    };
    
    import { ApiErrorTypes } from "@/errors/enums/error.enum";
    
    /**
     * Types for Error response
     */
    export type ErrorResponse = {
      message: string;
      code: number;
      type: ApiErrorTypes;
    };
    
    /**
     * Types for TW errors
     */
    export type ApiError = Pick<ErrorResponse, "message" | "code">;
    
    /**
     * Types for Wallet errors
     */
    export type WalletError = Pick<ErrorResponse, "message" | "code">;
    
    /**
     * Types for Request errors
     */
    export type RequestError = ApiError | Error | WalletError;
    
    /**
     * Get Escrow From Indexer By Contract Ids Params
     */
    export type GetEscrowFromIndexerByContractIdsParams = {
      /**
       * IDs (addresses) that identifies the escrow contracts.
       */
      contractIds: string[];
    
      /**
       * If true, the escrows will be validated on the blockchain to ensure data consistency.
       * This performs an additional verification step to confirm that the escrow data
       * returned from the indexer matches the current state on the blockchain.
       * Use this when you need to ensure the most up-to-date and accurate escrow information.
       * If you active this param, your request will take longer to complete.
       */
      validateOnChain?: boolean;
    };
    /**
     * Fund Escrow Payload, this can be a single-release or multi-release
     */
    export type FundEscrowPayload = {
      /**
       * Amount to be transferred upon completion of escrow milestones
       */
      amount: number;
    
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    };
    /**
     * Get Balance Params
     */
    export type GetBalanceParams = {
      /**
       * Addresses of the escrows to get the balance
       */
      addresses: string[];
    };

    Change Milestone Status

    Single & Multi Release

    /**
     * Change Milestone Status Payload, this can be a single-release or multi-release
     */
    export type ChangeMilestoneStatusPayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Index of the milestone to be updated
       */
      milestoneIndex: string;
    
      /**
       * New status of the milestone
       */
      newStatus: string;
    
      /**
       * New evidence of work performed by the service provider.
       */
      newEvidence?: string;
    
      /**
       * Address of the entity providing the service.
       */
      serviceProvider: string;
    };

    Release Funds

    Single Release

    /**
     * Single Release Release Funds Payload
     */
    export type SingleReleaseReleaseFundsPayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address of the user in charge of releasing the escrow funds to the service provider.
       */
      releaseSigner: string;
    };

    Multi Release

    /**
     * Multi Release Release Funds Payload
     */
    export type MultiReleaseReleaseFundsPayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address of the user in charge of releasing the escrow funds to the service provider.
       */
      releaseSigner: string;
    
      /**
       * Index of the milestone to be released
       */
      milestoneIndex: string;
    };

    Approve Milestone

    Single & Multi Release

    /**
     * Approve Milestone Payload, this can be a single-release or multi-release
     */
    export type ApproveMilestonePayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Index of the milestone to be updated
       */
      milestoneIndex: string;
    
      /**
       * New evidence of work performed by the service provider.
       */
      newEvidence?: string;
    
      /**
       * Address of the entity requiring the service.
       */
      approver: string;
    };

    Resolve Dispute

    Single Release

    /**
     * Resolve Dispute Payload
     */
    export type SingleReleaseResolveDisputePayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address in charge of resolving disputes within the escrow.
       */
      disputeResolver: string;
    
      /**
       * Distributions of the escrow amount to the receivers.
       */
      distributions: [
        {
          /**
           * Address of the receiver
           */
          address: string;
          /**
           * Amount to be transferred to the receiver. All the amount must be equal to the total amount of the escrow.
           */
          amount: number;
        },
      ];
    };

    Multi Release

    Start Dispute

    Single Release

    /**
     * Single Release Start Dispute Payload. This starts a dispute for the entire escrow.
     */
    export type SingleReleaseStartDisputePayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    };

    Multi Release

    /**
     * Multi Release Start Dispute Payload. This starts a dispute for a specific milestone.
     */
    export type MultiReleaseStartDisputePayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    
      /**
       * Index of the milestone to be disputed
       */
      milestoneIndex: string;
    };
    fund a multi-release escrow with testnet USDC
    create an SDK snippet to mark milestone as done
    explain the difference between approver and release signer
    /**
     * Multi Release Resolve Dispute Payload
     */
    export type MultiReleaseResolveDisputePayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address in charge of resolving disputes within the escrow.
       */
      disputeResolver: string;
    
      /**
       * Distributions of the escrow amount to the receivers.
       */
      distributions: [
        {
          /**
           * Address of the receiver
           */
          address: string;
          /**
           * Amount to be transferred to the receiver. All the amount must be equal to the total amount of the escrow.
           */
          amount: number;
        },
      ];
    
      /**
       * Index of the milestone to be resolved
       */
      milestoneIndex: string;
    };
    Platforms that could greatly benefit from the use of escrows don't use them because of the technical complexity and cost of building an escrow infrastructure.
    • Escrows with Fiat are next to impossible to build (Legacy escrows). Escrows are more commonly found on high value transactions, like Real Estate, Merge and Aquisitions, and cross border trade mainly because they are costly and sloe. Fiat requires bank accounts or a complex settlement system to achieve this functionality. I've met people who toook a year to build their escrow infrastrucute, A YEAR!

    • Blockchain obviously solves this, but complexity to built it is still high. Blockchain and web3 are the perfect solution for this, but we can't expect for every marketplace out there to have a Smart Contract engineer. Getting to experiment, test and scale with this technology is resource intensive and has a steep learning curve.

    🧩 What Escrow Solves

    • ❌ Chargebacks and fraud in marketplaces

    • ❌ Late or withheld payments for freelancers

    • ❌ Unclear fund control in grants, bounties, or pre-orders

    • ❌ No dispute path in P2P or milestone deals


    ✅ What Trustless Escrow Enables

    • 💸 Payments are only released when work is approved

    • 🔐 Funds are held in secure, neutral smart contracts

    • 🤖 Approval flows can be signed off by users, platforms, or agents

    • 🌍 Works globally with USDC and Stellar’s fast settlement

    Next Steps

    • Get Started: Quick Start Guide Begin integrating Trustless Work today.

    Simple representation of a Real Estate Escrow.
    Escrows on digital marketplaces.

    What You Can Do Here

    With Trustless Work, you can integrate smart escrows in minutes, this means you can:

    • Hold funds securely in non-custodial smart contracts.

    • Configure the roles to adapt to your business needs.

    • Launch way faster, without developing the smart contracts from scratch.

    • Not worry about audits, or time-consuming implementations.

    We abstract away the complexity of blockchain so you can focus on building great products.


    🗺️ What's in This Documentation?

    Section
    Purpose

    Quickstart

    Set up your first escrow

    Core Concepts

    Learn how Trustless Work escrows function

    API Reference

    Full technical docs, endpoints & schemas

    React SDK

    Use our SDK and hooks in your dApp

    Templates & Examples

    See working code and flows

    Dev Tools

    Explore our viewer, faucet, dApp, and more


    🧪 Try It in Minutes

    • 🔑 Request an API Key

    • 🧪 Explore the Demo dApp

    • 🧱 Clone the GitHub Starter


    🔗 Essential Links

    • 🌐 trustlesswork.com

    • 🔧 Swagger API Docs

    • 💬 Telegram Chat

    • 💎 Only Dust Profile

    • 📰

    We’ve organized everything you need into clear sections tailored to your role:

    1. Getting Started A quick-start guide to help you onboard with Trustless Work. Perfect for developers and businesses looking to explore the platform.

    2. API Reference Complete technical documentation for developers, including endpoints, code examples, and troubleshooting tips.

    3. Use Cases Discover how Trustless Work transforms industries, from marketplaces to crowdfunding.

    4. Technology Overview Dive into the technical architecture, featuring Stellar, Soroban, and smart escrows.

    5. Join our growing ecosystem, explore our open-source projects, and see where we’re headed next.


    Escrow products Mix & Match Guide

    From idea to escrow in one day.

    Designing an escrow is only half the battle. The real question is: How do you go from schema to a working product without burning months on contracts, audits, and ops flows?

    That’s why Trustless Work offers a modular product suite — tools you can combine like building blocks to launch escrow-powered workflows today.


    The Product Suite

    • Escrow API – The programmable core. Create, fund, update, approve, and release escrows. Maximum control, minimal friction.

    • Next.js SDK – React-friendly wrapper for interacting with escrows directly from your frontend.

    • Escrow Blocks – Pre-built React UI components. The fastest way to launch a user-facing escrow flow.

    • Back Office dApp (Open Source) – Ops-friendly control panel for deploying and managing escrows. Ideal for dispute resolution, MVP pilots, and non-technical teams. Use it, clone it.

    • Demo Lab dApp – Developer sandbox to test flows before pushing to production.

    • Escrow Viewer (Open Source) – Public, read-only explorer of escrow configs, milestones, and balances. Essential for transparency and compliance.


    Integration Models

    Think of these as playbooks for how to mix & match the suite:

    1. Full API/SDK Integration → For teams with dev capacity and UX control needs. Your UI + our escrow logic.

    2. Escrow Blocks: → Import template UI components to launch faster, with minimal custom frontend work.

    3. Hybrid Back Office → Your users stay in your UI, but ops/disputes handled in the Back Office.

    4. Back Office First → Launch this week. Manage flows directly in our Back Office while validating.

    Single Release Escrow

    Single-Release Escrow is a type in which all your funds are released only once, either with the resolution of a dispute or by completing all the milestones defined for it.

    The Deploy endpoints allow users to deploy escrows efficiently. These endpoints provide the way to initialize escrows in the Stellar's Blockchain.

    Key Components

    • Initial Fund Lockup: Upon contract initiation, the escrow amoun plus the platform fee (“platformFee”) is deposited into an escrow account.

    • Flags: The escrow status is interpreted by means of these flags: (approved, dispute, released, resolved).

    • Primary Roles:

      • Service Provider: Delivers the deliverable corresponding to each milestone.

      • Approver: Verifies and approves a milestone before authorizing the release of funds.

      • Dispute Resolver: Intervenes in case of disagreement and decides whether to release or refund the locked amount.

    Brief Workflow

    1. An escrow is initialized by defining all the necessary escrow properties.

    2. The Service Provider completes a milestone and requests approval.

    3. The approver reviews the deliverable; if approved, signs a transaction that releases the amount allocated as escrow reward (minus the platform and Trustless Work fee).

    4. The Stellar network executes the transaction and transfers the payment to the Service Provider or the configured Receiver.

    This model protects all parties: the client knows that funds are available but cannot be released without validation, and the service provider receives payment upon completion of all milestones and the milestones themselves being approved by the approver, leveraging Stellar's transparency and immutability.

    Get Multiple Escrow Balance

    Get the balance of multiple escrows.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    <token>

    Open API

    Use Example:

    Get Escrows By Contract Ids

    Returns all the information of a security deposit requested through one or more requested contract ids.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    Open API

    Use Example:

    Get Escrows By Role

    Returns all the information of an escrow requested through the role.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    Open API

    Use Example:

    Introduction

    How to get started in Trustless Work API REST

    Base URL

    Mainnet API

    https://api.trustlesswork.com

    Testnet API

    https://dev.api.trustlesswork.com

    Warning!

    You'll have 50 as a request rate limit per client in the API every 60 seconds.

    GitHub Repository

    API Swagger

    Architecture

    Step-by-step instructions to help you connect your product with Trustless Work smoothly and efficiently.

    Overview

    The purpose of this document is to provide a comprehensive guide on implementing best practices within the development team. It covers essential methodologies, tools, and strategies that can enhance productivity and ensure high-quality outcomes.

    Basic Flow

    Most common flow in the dApps.


    Services - Endpoints

    Flow that must always be executed at each Endpoint except Get Balances & Get Escrow

    The document describes the essential execution flow for service endpoints, with exceptions for Get Balances and Get Escrow, ensuring uniformity in implementation.

    Trustlines

    On Stellar, accounts must explicitly opt in to hold and use assets. This opt-in is called a trustline.

    What Are Trustlines in Stellar?

    • A trustline is an explicit opt-in setup by a Stellar account that authorizes it to hold, receive, and transact with a non‑native asset (i.e. anything other than XLM), issued by a specific issuer

    Dispute Milestone

    Responsible for setting the milestone in dispute state. Changes the value of the milestone's "flags.disputed" property to true.

    Headers

    Name
    Value

    Approve Milestone

    Responsible for modifying the "flag" property of a specific milestone in the escrow to approve that milestone.

    Headers

    Name
    Value

    Stellar Wallets

    A Comprehensive Developer's Guide to Stellar Wallet Integrations

    Introduction to Stellar Wallets

    Stellar wallets are essential tools for interacting with the Stellar blockchain, enabling developers and users to securely manage, send, and receive digital assets. This section covers the setup and integration of popular Stellar wallets:

    Get Escrows By Signer

    Returns all the information of an escrow requested through the signer.

    Headers

    Name
    Value

    Resolve Milestone Dispute

    Resolves escrow milestone disputes by distributing funds to the approver and service provider as determined by the dispute resolver.

    Headers

    Name
    Value

    Indexer

    This endpoint facilitates the recovery and proper storage in Firebase of escrow-related information submitted to the Stellar Blockchain via external applications, bypassing the standard application.

    Key Components

    • External Transaction: This endpoint handles cases where the XDR generated by any endpoint of our REST API is signed and sent directly by external applications.

    Fund Escrow

    Allows users to deposit funds into an existing escrow contract, securing them until the agreed conditions are met.

    Headers

    Name
    Value

    Update From Tx Hash

    This endpoint allows you to change the properties of an escrow as long as a series of requirements are met, which will be mentioned in this section.

    Requirements for Use:

    • You must have the valid txHash generated from a transaction sent directly to the Stellar network.

  • Template Fork → Clone one of our open-source dApps, rebrand, extend. Perfect for hackathons.

  • Receiver: The final recipient of the funds if different from the Service Provider.

    If a dispute arises, the Dispute Resolver evaluates the evidence and, upon signing their decision, marks the escrow as resolved to release or refund the corresponding funds.

    Use Cases

    See how escrows are used across industries

    Community

    Join the movement and build with us

    Escrow Times Blog
    Community and Roadmap

    Albedo Wallet

  • xBull Wallet

  • Rabet Wallet

  • Lobstr Wallet

  • Hana Wallet

  • Key Concepts

    Public and Private Keys

    • Public Key: Your Stellar address, used to receive assets. This can be shared publicly.

    • Private Key: Your secret key used to authorize transactions. This must be kept secure and never shared.

    • Never share your private key with anyone

    Trustlines

    Stellar accounts need to establish trustlines to receive custom assets. Trustlines represent a relationship between two accounts, where one account trusts the other to issue a specific asset. This allows the receiving account to accept and hold that asset.

    Trustlines are crucial in Stellar for:

    • Establishing trust with asset issuers

    • Enabling receipt of custom tokens

    • Requiring a small minimum balance to create

    Minimum Balance Requirements

    • Stellar accounts require a minimum balance (currently 0.5 XLM)

    • Each trustline adds to the minimum balance requirement

    • Helps prevent spam and ensures network stability

    Security Best Practices

    • Use hardware wallets when possible

    • Enable two-factor authentication

    • Store private keys offline

    • Use secure, updated browsers

    • Regularly update wallet software

    • Be cautious of phishing sites

    Freighter Wallet

    Get Escrows by Signer

    Params

    /**
     * Get Escrows From Indexer By Signer Params
     */
    export type GetEscrowsFromIndexerBySignerParams = {
      /**
       * Page number. Pagination
       */
      page?: number;
    
      /**
       * Sorting direction. Sorting
       */
      orderDirection?: "asc" | "desc";
    
      /**
       * Order by property. Sorting
       */
      orderBy?: "createdAt" | "updatedAt" | "amount";
    
      /**
       * Created at = start date. Filtering
       */
      startDate?: string;
    
      /**
       * Created at = end date. Filtering
       */
      endDate?: string;
    
      /**
       * Max amount. Filtering
       */
      maxAmount?: number;
    
      /**
       * Min amount. Filtering
       */
      minAmount?: number;
    
      /**
       * Is active. Filtering
       */
      isActive?: boolean;
    
      /**
       * Escrow that you are looking for. Filtering
       */
      title?: string;
    
      /**
       * Engagement ID. Filtering
       */
      engagementId?: string;
    
      /**
       * Status of the single-release escrow. Filtering
       */
      status?: SingleReleaseEscrowStatus;
    
      /**
       * Type of the escrow. Filtering
       */
      type?: "single-release" | "multi-release";
    
      /**
       * If true, the escrows will be validated on the blockchain to ensure data consistency.
       * This performs an additional verification step to confirm that the escrow data
       * returned from the indexer matches the current state on the blockchain.
       * Use this when you need to ensure the most up-to-date and accurate escrow information.
       * If you active this param, your request will take longer to complete.
       */
      validateOnChain?: boolean;
    
      /**
       * Address of the user signing the contract transaction.
       */
      signer: string;
    };
    Without a trustline, an account cannot receive or keep that asset on the Stellar network
  • Each trustline requires 0.5 XLM in base reserve, increasing the minimum balance and limiting abuse

  • Trustlines also include a trust limit—the maximum amount the account is willing to hold—and record the current balance and liabilities (e.g., open offers)

  • Without a trustline, an account cannot receive or hold a token like USDC.


    🔑 Why Trustlines Matter

    • Authorization: They give permission for an account to hold a specific asset (e.g., USDC from its issuer).

    • Reserves: Each trustline requires a small XLM reserve, so accounts can’t spam unlimited assets.

    • Limits: A trustline sets a maximum balance the account is willing to hold.


    ⚡ Trustlines in Escrows

    • Escrows depend on trustlines.

    Our escrows can be configured for ANY trustline on Stellar. But all roles must have the Trustline with that asset.

    • Practical impact: All participants must have the proper trustline set up first.

    ✍🏼 USDC/EURC Trustline

    USDC/EURC is the most functional and widely used trustline for escrow. I am attaching the issuer addresses for these so that you can use them when initializing an escrow and defining your trustline:

    USDC

    Testnet: GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5

    Mainnet: GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN

    EURC

    Testnet: GB3Q6QDZYTHWT7E5PVS3W7FUT5GVAFC5KSZFFLPU25GO7VTC3NM2ZTVO

    Mainnet: GDHU6WRG4IEQXM5NZ4BMPKOXHW76MZM4Y2IEMFDVXBSDP6SJY4ITNPP2

    References:


    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Content-Type

    application/json

    x-api-key

    <token>

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Content-Type

    application/json

    x-api-key

    <token>

    Open API

    Use Example:

    Content-Type

    application/json

    x-api-key

    <token>

    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
    
        const data = await http.get(
          "/escrow/single-release/get-escrows-by-signer?signer=GPUACN...." 👈🏼 // All required parameters are passed at the query level.
        ); 
      
        return data;
    }

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Content-Type

    application/json

    x-api-key

    <token>

    Internal Queue: Information initially generated by the application is stored in an internal queue, awaiting association with a specific transaction identified by its hash (txHash).
  • Firebase Storage: Once retrieved from the internal queue, the information is permanently stored in Firebase.

  • Roles Involved

    • External Application: Signs and sends the XDR without using the standard endpoint (helper/send-transaction).

    • Indexer: Responsible for retrieving information from the internal queue and storing it in Firebase upon receiving the transaction hash (txHash).

    Endpoint Workflow

    1. The XDR is obtained from any transaction (not signed) generated with any of the endpoints of our REST API.

    2. The generated XDR is signed and sent externally (without using helper/send-transaction).

    3. The external application provides the corresponding txHash.

    4. The txHash is sent to indexer/update-from-txHash.

    5. The endpoint retrieves information stored in the internal queue and saves it in Firebase.

    Usage Example

    • Request:

    • Response:

    The information associated with the provided txHash is successfully stored in Firebase, ensuring the integrity of the generated escrow.

    Benefits of Usage

    • Enables external integrations while maintaining information consistency.

    • Prevents data loss when bypassing the standard workflow.

    • Ensures secure and accurate storage in Firebase using the transaction hash.

    This endpoint enhances the system's flexibility and robustness, ensuring all transactions, regardless of the method used, are adequately recorded in Firebase.

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Content-Type

    application/json

    x-api-key

    <token>

    The escrow data associated with this txHash must already exist in the internal queue.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    <token>

    Workflow:

    1. The XDR is obtained from any transaction (not signed) generated with any of the endpoints of our REST API.

    2. An unsigned XDR is generated and returned.

    3. The XDR is signed externally and sent directly to Stellar.

    4. The resulting txHash is retrieved.

    5. The txHash is sent to /indexer/update-from-txHash.

    6. The escrow information is retrieved from the internal queue and stored in Firebase.

    Open API

    Use Example:

    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async (addresses: string[]) => {
      const response = await http.get("/helper/get-multiple-escrow-balance", {
        params: { addresses },
      });
    
      return response;
    };
    

    <token>

    <token>

    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
    
        const data = await http.get(
          "/escrow/single-release/get-escrows-by-role?role=approver?roleAddress=GPUACN...." 👈🏼 // All required parameters are passed at the query level.
        ); 
      
        return data;
    }

    Change Milestone Status

    Responsible for modifying the "status" property of a specific milestone in the escrow.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use example:

    Change Milestone Status

    Responsible for changing the milestone status of an escrow through the service provider.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use example:

    Send Transaction

    Most Trustless Work endpoints return an unsigned transaction in XDR format. This endpoint is used to sign such unsigned transactions and send them to the Stellar network.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    Open API

    Use Example:

    Release Funds

    You release the escrow funds to the service provider through the approver.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    <token>

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Fund Escrow

    Allows users to deposit funds into an existing escrow contract, securing them until the agreed conditions are met.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    useGetEscrowsFromIndexerBySigner

    Returns the escrows that you're looking for. It comes from our indexer (database) synchronizer with the blockchain.

    Usage

    This custom hook exposes a function to get the escrows that you are looking obtain.

    import { useGetEscrowsFromIndexerBySigner } from "@trustless-work/escrow/hooks";
    import { GetEscrowsFromIndexerBySignerParams} from "@trustless-work/escrow/types";
    
    /*
     *  useGetEscrowsFromIndexerBySigner
    */
    const { getEscrowsBySigner } = useGetEscrowsFromIndexerBySigner();
    
    /* 
     * It returns the escrows that you are looking for
     * payload should be of type `GetEscrowsFromIndexerBySignerParams`
    */
    await getEscrowsBySigner(payload);

    Function

    • getEscrowsBySigner Responsible for building and returning data based on the provided payload.

    Argument:

    GetEscrowsFromIndexerBySignerParams: An object containing the required fields to get the escrows by signer.

    Return Value:

    escrows: The escrows that you are looking for.


    Usage ExampleForm

    Resolve Dispute

    Resolves escrow disputes by distributing funds to the approver and service provider as determined by the dispute resolver.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Withdraw Remaining Funds

    This function is used to withdraw funds that are stuck in escrow and cannot be withdrawn due to the way multi-release escrow works, since disputes in this case are handled at the milestone level.

    In single-release escrow accounts, this endpoint is not necessary because disputes are handled at the escrow account level, so if there is any remaining balance in the contract, a dispute is opened and the remaining balance is withdrawn.

    Headers

    Name
    Value

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Release Milestone Funds

    You release the milestone escrow funds to the service provider through the approver.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    <token>

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    useFundEscrow

    Allows users to deposit funds into an existing escrow contract, securing them until the agreed conditions are met.

    Usage

    This custom hook exposes a function to fund and escrow.

    Mutation Function

    fundEscrow

    Returning an unsigned transaction based on the provided payload.

    EscrowType: Specifies the type of escrow. It accepts the following values:

    • multi-release: Allows for multiple releases of funds.

    • single-release: Funds are released in a single transaction.

    FundEscrowPayload: An object with fields necessary to fund an escrow. It is applicable for both single-release and multi-release escrow types.

    Parameters:

    • type: Describes the escrow type to be used. Options are "multi-release" or "single-release".

    • payload: Contains the data required for fund escrow.

    Return Value:

    unsignedTransaction: An object representing the constructed transaction, ready to be signed by your wallet and broadcast.


    Usage Example

    Dispute Escrow

    Responsible for initiating a dispute in an escrow. Change the value of the flag “disputed” from “disputed” to true.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Multi Release Escrow

    A Multi-Release Contract is an escrow agreement on the Stellar blockchain that divides the total payment of a project into multiple deliveries (“milestones”). Each milestone is released only upon verification of its completion, ensuring that funds remain secure until the associated work is validated.

    Key Components

    • Initial Fund Lockup: Upon contract initiation, the total of all milestone amounts plus the platform fee (“platformFee”) is deposited into an escrow account.

    • Milestones: Each stage includes a description, a specific amount, and status flags (approved, dispute, released, resolved).

    • Primary Roles:

      • Service Provider: Delivers the deliverable corresponding to each milestone.

      • Approver: Verifies and approves a milestone before authorizing the release of funds.

      • Dispute Resolver: Intervenes in case of disagreement and decides whether to release or refund the locked amount.

    Brief Workflow

    1. The Service Provider completes a milestone and requests approval.

    2. The Approver reviews the deliverable; if approved, they sign a transaction that releases only the amount allocated to that milestone (minus the platform and Trustless Work fee).

    3. The Stellar network executes the transaction and transfers the payment to the Service Provider or the configured Receiver.

    4. If a dispute arises, the Dispute Resolver evaluates the evidence and, upon signing their decision, marks the milestone as resolved

    This model protects all parties: the client knows that funds are available but cannot be released without validation, and the Service Provider receives payment for each verified delivery—leveraging Stellar’s transparency and immutability.

    Integration checklist

    Goal: Go from idea to live escrows in under 1 week.

    Time Estimate: 4–8 hours of implementation (plus testing).

    Prerequisites:

    • Basic Web3 knowledge

    • A Stellar wallet (Freighter, Albedo)

    Request API Key

    Request an API Key to interact with all the endpoints.

    To interact with the Trustless Work API, you’ll need to generate an API Key. This key authenticates your requests and links them to your verified wallet identity. Overview: API keys are managed directly in the Trustless Work BackOffice dApp. They are required only if you plan to interact programmatically with the API — you don’t need one for using the dApp interface itself.


    Step 1 — Connect Your Stellar Wallet

    To begin, connect a Stellar-compatible wallet (such as Freighter, Albedo, or xBull

    Approval phase

    Phase 4 — Approval (Validating Progress and Unlocking Readiness)

    After the Service Provider updates a milestone’s status, the Approver steps in. This is the phase where intent meets validation — the moment a platform or client officially confirms that progress is satisfactory.

    Approval is the green light that tells the escrow:

    “This milestone has met the conditions. It can now move toward payment.”


    Release phase

    Phase 5 — Release (Executing the Payout)

    The Release Phase is where everything comes together — approvals turn into payouts, and logic turns into money movement. This is the only phase that actually moves funds out of the escrow and into the hands of the receivers.

    It’s also the most restricted phase:

    Only the Release Signer can execute the release.


    Freighter Wallet

    Official logo of the Freighter Wallet.

    Freighter is a browser extension wallet for Stellar. It is a non-custodial wallet extension for your browser.


    What You’ll Learn

    • How to install and set up Freighter Wallet.

    Architecture & Design Strategy

    Building Hybrid — The Power of Decentralized Foundations

    Most platforms don’t need to “go fully on-chain.” What they need is programmable trust—a neutral, verifiable layer that handles what matters most: custody, release logic, and auditability.

    That’s what Trustless Work provides. We let you decide how decentralized you want to go — from a quick no-code back office setup to a fully automated, API-driven architecture.


    Get Escrows by Role

    Params

    Approve Milestone

    Responsible for approving the milestone through the escrow approver.

    Headers

    Name
    Value
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        // Execute the endpoint
        const response = await http.post(
          "/escrow/multi-release/dispute-milestone",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/escrow/multi-release/approve-milestone",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/escrow/multi-release/resolve-milestone-dispute",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    {
      "txHash": "your-txHash-value"
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/escrow/single-release/fund-escrow",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const updateEscrowFromTxHash = async (txHash) => {
      const response = await http.post("/indexer/update-from-txHash", { txHash });
      return response.data;
    };
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
      const contractIds = [
        "CCR6HLU3LQMXOESNA6TOS2RZKGEBWQG3EN5FMZNC43RVXZWTTDCZ...",
        "CCA7WTCVCQ5JPKNIFSHPSJLJ3FJ3GKPNEVAIHP6T..."
      ];
      const validateOnChain = true;
    
      const params = new URLSearchParams();
      contractIds.forEach(id => params.append("contractIds[]", id));
      params.append("signer", signer);
      params.append("validateOnChain", validateOnChain.toString());
    
      const response = await http.get(`/helper/get-escrow-by-contract-ids?${params.toString()}`);
      return response;
    }
    import { useFundEscrow } from "@trustless-work/escrow/hooks";
    import { FundEscrowPayload } from "@trustless-work/escrow/types";
    
    /*
     *  useFundEscrow
    */
    const { fundEscrow } = useFundEscrow();
    
    /* 
     * It returns an unsigned transaction
     * payload should be of type `fundEscrow`
    */
    const { unsignedTransaction } = await fundEscrow(payload);
    

    Receiver: The final recipient of the funds if different from the Service Provider.

    to release or refund the corresponding funds.
    Access to stablecoins (USDC/EURC on testnet or mainnet)

    Phase 1 – Preparation (Setup & Planning)

    1. Understand Your Use Case

    📄 Escrow Lifecycle

    2. Choose the Correct Escrow Type

    3. Define Roles

    Assign the parties' Stellar addresses to each role in your escrow:📄 Roles in Trustless Work

    4. Define Escrow Properties

    Escrow Properties


    Phase 2 – Integration (Core Setup)

    5. Get Access

    6. Install SDK / Tools Getting Started - SDK

    7. Configure Your Escrow


    Phase 3 – Testing (Validation & Debugging)

    8. Deploy on Testnet

    9. Simulate Edge Cases

    10. Run Compliance & UX Review


    Phase 4 – Launch (Go Live)

    11. Migrate to Mainnet

    12. Monitor & Optimize



    🧾 What Approval Means

    Approving a milestone doesn’t move funds yet — it simply updates the milestone’s internal flag: approved: true

    That single flag transforms the milestone from in progress to ready for release.

    It’s a lightweight change in data but a heavy one in meaning — because once approved, the milestone is permanently recorded as validated. There’s no “unapprove” function. The decision becomes part of the escrow’s history.


    👤 Who Approves

    Only the Approver — the wallet assigned to that role — can sign the approval. This address is often:

    • The buyer in a freelance contract,

    • The sponsor in a grant,

    • Or the platform logic in automated or multi-party flows.

    The Approver’s signature confirms that:

    1. The milestone has been delivered satisfactorily, and

    2. The platform can now safely move toward release.


    🔁 How Approval Works Across Escrow Types

    Single-Release Escrow

    • All milestones must be approved before any funds can move.

    • Once every milestone carries the approved: true flag, the escrow becomes “ready for release.”

    • The Release Signer can then execute the payout in one transaction.

    Multi-Release Escrow

    • Each milestone has its own approval and release logic.

    • Approving one milestone makes that milestone’s funds eligible for release, regardless of others.

    • This allows multiple, independent approval-release cycles within the same escrow.

    🧩 In short:

    • Single-Release → approval is collective (all or nothing).

    • Multi-Release → approval is modular (one milestone at a time).


    🪶 The Freedom of Approval Timing

    Approvals can happen at any moment, regardless of the milestone’s current “status” text.

    Even if the Service Provider used a custom status like “Under Review” or “In Transit”, the Approver can sign approval immediately if they’re satisfied.

    That flexibility allows each platform to define its own logic — maybe auto-approving after a timer, or requiring manual review before payment.

    Once approved:

    • The milestone’s approved flag turns true,

    • The escrow recognizes that milestone as complete,

    • And it remains approved for the rest of its lifecycle.


    🧩 Relation to Disputes and Release

    Approval is also what separates smooth transactions from disputes. If the Approver signs, the flow advances to Release. If they refuse or challenge, the same milestone can instead move into Dispute Resolution.

    🧭 Approval is the fork in the road — One path leads to payment, the other to mediation.


    📦 Outcome of the Approval Phase

    By the end of this phase:

    • The milestone’s approved flag is set to true.

    • The escrow recognizes that milestone as ready for release.

    • The approval event is permanently logged on-chain.

    • Participants can view the approval in real time through the Escrow Viewer.

    💡 Approval doesn’t release funds — it unlocks the ability to. It’s the signal that work is accepted, and the escrow can now fulfill its purpose.

    🔑 Who Can Release Funds

    Every escrow designates one address as the Release Signer. That wallet — and only that wallet — can authorize the transfer of funds out of the escrow contract.

    Depending on your workflow, this role can be configured in two main ways:

    • As a “push” model — the platform or a neutral signer triggers the release to the receiver.

    • As a “claim” model — the receiver is also the release signer, meaning they can claim their own funds once approved.

    Both options are valid, and each suits a different kind of use case:

    Use Case
    Release Pattern
    Example

    Freelance marketplaces

    Push

    Platform acts as release signer, paying out after approval

    Escrow-based payouts or grants

    Claim

    Receivers themselves trigger withdrawal

    Automated dApps or DAO tooling

    Push or Claim

    Logic bots or scripts trigger release conditions automatically


    🧭 What Must Be True Before Release

    The escrow enforces strict verification before funds can move.

    Single-Release Escrow

    • All milestones must have their approved flag set to true.

    • No milestone can be in dispute.

    • Once verified, the contract releases the entire escrowed amount (minus fees) to the receiver.

    Multi-Release Escrow

    • Only the milestone(s) being released need to be approved.

    • Each milestone can be released independently.

    • The contract disburses only the approved milestone’s amount to its corresponding receiver.

    🧩 In essence:

    • Single-Release: “Release everything.”

    • Multi-Release: “Release just this part.”


    ⚙️ What Happens When Release is Signed

    When the Release Signer executes the transaction:

    1. The contract verifies all conditions.

    2. It calculates deductions:

      • Platform Fee (set during initiation, e.g., 1%)

      • Trustless Work Fee (protocol fee, fixed at 0.3%)

    3. It transfers the remaining balance to the receiver’s address.

    4. It updates the milestone (or entire escrow) with:

      • released: true

      • A release transaction hash (visible on-chain).

    This event becomes a permanent, auditable record of payout completion.


    💬 Push vs. Claim — Two Faces of Release

    Push

    • The Release Signer (platform or operator) sends funds out proactively.

    • Ideal for platforms that handle fund flow on behalf of users.

    • Provides an extra layer of control and compliance.

    Claim

    • The Receiver is also the Release Signer.

    • They simply “claim” their approved funds directly from escrow.

    • Ideal for trust-minimized environments, grants, or bounty-style setups.

    🧠 Both flows coexist within Trustless Work. The release logic doesn’t care who presses the button — only that the signer has permission.


    🌐 Visibility and Traceability

    Every release emits a Release Event — a blockchain record containing:

    • The escrow ID (contract address)

    • The milestone(s) released

    • The receiver address

    • Amount sent

    • Platform and protocol fees deducted

    You can view these transparently through:

    • Escrow Viewer — human-readable milestone and release records

    • Stellar Expert — raw transaction details for verification and audit trails


    📦 Outcome of the Release Phase

    By the end of this phase:

    • The approved milestones (or full escrow) have been paid out.

    • Platform and Trustless Work fees have been distributed.

    • The escrow contract updates its flags (released: true) accordingly.

    • A complete payout record is available both on-chain and in the Viewer.

    💡 The Release Phase is where trust becomes settlement — money leaves the neutral zone and reaches its rightful destination.

    https://github.com/Trustless-Work/react-library-trustless-workgithub.com
    https://www.npmjs.com/package/@trustless-work/escrowwww.npmjs.com
    Get Escrows by Signer
    Release Funds
    /**
     * Get Escrows From Indexer By Role Params
     */
    export type GetEscrowsFromIndexerByRoleParams = {
      /**
       * Page number. Pagination
       */
      page?: number;
    
      /**
       * Sorting direction. Sorting
       */
      orderDirection?: "asc" | "desc";
    
      /**
       * Order by property. Sorting
       */
      orderBy?: "createdAt" | "updatedAt" | "amount";
    
      /**
       * Created at = start date. Filtering
       */
      startDate?: string;
    
      /**
       * Created at = end date. Filtering
       */
      endDate?: string;
    
      /**
       * Max amount. Filtering
       */
      maxAmount?: number;
    
      /**
       * Min amount. Filtering
       */
      minAmount?: number;
    
      /**
       * Is active. Filtering
       */
      isActive?: boolean;
    
      /**
       * Escrow that you are looking for. Filtering
       */
      title?: string;
    
      /**
       * Engagement ID. Filtering
       */
      engagementId?: string;
    
      /**
       * Status of the single-release escrow. Filtering
       */
      status?: SingleReleaseEscrowStatus;
    
      /**
       * Type of the escrow. Filtering
       */
      type?: EscrowType;
    
      /**
       * If true, the escrows will be validated on the blockchain to ensure data consistency.
       * This performs an additional verification step to confirm that the escrow data
       * returned from the indexer matches the current state on the blockchain.
       * Use this when you need to ensure the most up-to-date and accurate escrow information.
       * If you active this param, your request will take longer to complete.
       */
      validateOnChain?: boolean;
    
      /**
       * Role of the user. Required
       */
      role: Role;
    
      /**
       * Address of the owner of the escrows. If you want to get all escrows from a specific role, you can use this parameter. But with this parameter, you can't use the signer parameter.
       */
      roleAddress: string;
    };
    

    Responses

    Escrow's Response Entity

    src/hooks/useGetEscrowsFromIndexerBySigner.ts
    import {
      useGetEscrowsFromIndexerBySigner,
    } from "@trustless-work/escrow/hooks";
    import {
      GetEscrowsFromIndexerBySignerParams, 
    } from "@trustless-work/escrow/types";
    
    export const useGetEscrowsFromIndexerBySignerForm = () => {
    
     /*
      *  useGetEscrowsFromIndexerBySigner
     */
     const { getEscrowsBySigner } = useGetEscrowsFromIndexerBySigner();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: GetEscrowsFromIndexerBySignerParams) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the getEscrowsBySigner function
           * - The result will be an escrow
          */
          const escrows = await getEscrowsBySigner(payload);
          
          if (!escrows) {
            throw new Error("Escrows not found");
          }
    
          /**
           * @Responses:
           * escrows !== null
           * - Escrows received successfully
           * - Show a success toast
           *
           * escrows === null
           * - Show an error toast
           */
          if (escrows) {
            toast.success("Escrows Received");
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    src/hooks/useFundEscrowForm.ts
    import {
      useFundEscrow,
      useSendTransaction,
    } from "@trustless-work/escrow/hooks";
    import {
      FundEscrowPayload
    } from "@trustless-work/escrow/types";
    
    export const useFundEscrowForm = () => {
    
     /*
      *  useFundEscrow
     */
     const { fundEscrow } = useFundEscrow();
     
     /*
      *  useSendTransaction
     */
     const { sendTransaction } = useSendTransaction();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: FundEscrowPayload) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the fundEscrow function
           * - The result will be an unsigned transaction
           */
          const { unsignedTransaction } = await fundEscrow(
            payload,
            "multi-release"
            // or ...
            // "single-release"
          );
    
          if (!unsignedTransaction) {
            throw new Error(
              "Unsigned transaction is missing from fundEscrow response."
            );
          }
    
          /**
           * @Note:
           * - We need to sign the transaction using your [private key] such as wallet
           * - The result will be a signed transaction
           */
          const signedXdr = await signTransaction({ /* This method should be provided by the wallet */
            unsignedTransaction,
            address: walletAddress || "",
          });
    
          if (!signedXdr) {
            throw new Error("Signed transaction is missing.");
          }
    
          /**
           * @Note:
           * - We need to send the signed transaction to the API
           * - The data will be an SendTransactionResponse
           */
          const data = await sendTransaction(signedXdr);
    
          /**
           * @Responses:
           * data.status === "SUCCESS"
           * - Escrow funded successfully
           * - Show a success toast
           *
           * data.status == "ERROR"
           * - Show an error toast
           */
          if (data.status === "SUCCESS") {
            toast.success("Escrow Funded");
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    import { Date, EscrowType, Status } from "./types";
    import {
      Flags,
      MultiReleaseEscrow,
      MultiReleaseMilestone,
      Roles,
      SingleReleaseEscrow,
      SingleReleaseMilestone,
      Trustline,
    } from "./types.entity";
    
    /**
     * Escrow's Response like fund, release, change, etc ...
     */
    export type EscrowRequestResponse = {
      /**
       * Status of the request
       */
      status: Status;
    
      /**
       * Unsigned transaction
       */
      unsignedTransaction?: string;
    };
    
    /**
     * Send Transaction Response
     */
    export type SendTransactionResponse = {
      /**
       * Status of the request
       */
      status: Status;
    
      /**
       * Message of the request
       */
      message: string;
    };
    
    /**
     * Initialize Escrow Response
     */
    export type InitializeSingleReleaseEscrowResponse = EscrowRequestResponse & {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Escrow data
       */
      escrow: SingleReleaseEscrow;
    
      /**
       * Message of the request
       */
      message: string;
    };
    
    /**
     * Initialize Multi Release Escrow Response
     */
    export type InitializeMultiReleaseEscrowResponse =
      InitializeSingleReleaseEscrowResponse & {
        /**
         * Escrow data
         */
        escrow: MultiReleaseEscrow;
      };
    
    /**
     * Update Escrow Response
     */
    export type UpdateSingleReleaseEscrowResponse =
      InitializeSingleReleaseEscrowResponse;
    
    /**
     * Update Multi Release Escrow Response
     */
    export type UpdateMultiReleaseEscrowResponse =
      InitializeMultiReleaseEscrowResponse;
    
    /**
     * Get Balances Response
     */
    export type GetEscrowBalancesResponse = {
      /**
       * Address of the escrow
       */
      address: string;
    
      /**
       * Balance of the escrow
       */
      balance: number;
    };
    
    /**
     * Get Escrows From Indexer Response
     */
    export type GetEscrowsFromIndexerResponse = {
      signer?: string;
      contractId?: string;
      engagementId: string;
      title: string;
      roles: Roles;
      description: string;
      amount: number;
      platformFee: number;
      balance?: number;
      milestones:
        | SingleReleaseMilestone[]
        | (MultiReleaseMilestone[] & { disputeStartedBy: Roles });
      flags?: Flags;
      trustline: Trustline & { name: string };
      receiverMemo?: number;
      disputeStartedBy?: string;
      fundedBy?: string;
      isActive?: boolean;
      approverFunds?: string;
      receiverFunds?: string;
      user: string;
      createdAt: Date;
      updatedAt: Date;
      type: EscrowType;
    };
    
    /**
     * Response for updating escrow from transaction hash
     */
    export type UpdateFromTxHashResponse = {
      /**
       * Status of the request
       */
      status: "SUCCESS" | "FAILED";
    
      /**
       * Message describing the result
       */
      message: string;
    };
    ) to the dApp. You’ll be prompted to
    sign a message
    — this confirms ownership of your wallet and automatically creates your
    user profile
    .

    If you’ve never used a Stellar wallet before, check out the Stellar Wallets section for setup instructions.


    Step 2 — Complete Your Profile

    Once logged in:

    1. Click your wallet address at the bottom-left corner.

    2. Select Settings from the menu.

    3. Fill out your profile with basic details — name, email, and use case.

      • The Use Case field is required before you can generate an API key.

      • This helps us understand your integration goals and provide better support.

    💡 Tip: You can always update your profile later to reflect new projects or integrations.


    Step 3 — Request an API Key

    In the Settings sidebar, navigate to the API Keys tab:

    1. Choose a Network:

      • Testnet — For development and testing

      • Mainnet — For production (available post-audit)

    2. Click Request API Key to generate a new key.

    3. Copy it immediately — once you close the dialog, it cannot be viewed again for security reasons. You’ll need to generate a new one if lost.

    ⚠️ You must confirm that you’ve copied the key before exiting the dialog.

    You must have at least the use case filled, if not, the system won't give you the API Key.


    🧠 Summary

    Action
    Description

    Connect Wallet

    Log in and sign to create your profile.

    Complete Profile

    Fill in personal info and use case (required).

    Request Key

    Generate a key from the API Keys tab.

    Save Securely

    Copy and store it safely — it’s shown only once.


    ✅ Ready to Build

    Once you have your API key, you can start interacting with the Trustless Work API to:

    • Deploy and fund escrows

    • Mark milestones as complete

    • Approve, dispute, or release payments

    • Query escrow status and balances

    Explore the API Reference section to see available endpoints.

    https://dapp.trustlesswork.comdapp.trustlesswork.com
    Stellar Wallets

    How to connect Freighter Wallet to Trustless Work.

  • Useful resources, security tips, and FAQs.


  • Installation

    Step-by-Step Instructions:

    1. Open the official Freighter Wallet website.

    2. Click on "Add to Browser" for your preferred browser (e.g., Chrome, Brave, or Firefox).

      • Ensure you download only from the official website to avoid scams.

    3. Follow the browser prompts to install the extension.

    4. After installation, pin the Freighter extension for easy access.

    Freighter Preview

    Setting Up Freighter Wallet

    Creating a New Wallet

    1. Open the Freighter extension by clicking on its icon in your browser.

    2. Click on "Create New Wallet".

    3. Set a secure password (store this password securely).

    4. Freighter will generate a Recovery Phrase (also called a Seed Phrase).

      • Write it down and store it in a safe place. Do not share it with anyone.

    Freighter Setup
    Freighter Setup

    Importing an Existing Wallet

    1. Open the Freighter extension.

    2. Click on "Import Wallet".

    3. Enter your existing Seed Phrase and set a password.

    Freighter Setup

    Connecting Freighter to Trustless Work

    1. Navigate to the Trustless Work platform.

      • Example link: Trustless Work.

    2. Click "Connect Wallet" in the top-right corner of the page.

    3. Select "Freighter Wallet" from the list of options.

    4. A pop-up will appear from Freighter asking for confirmation.

    5. Approve the connection in the wallet extension.

    Trustless Work
    Wallet

    Note:

    • Ensure Freighter is set to the correct network (Testnet or Mainnet) based on your environment. You can toggle the network in the Freighter settings.


    Best Practices and Security Tips

    • Backup Your Seed Phrase: Store it in a secure, offline location.

    • Use Testnet for Development: When testing or experimenting, always switch to the Testnet to avoid losing real funds.

    • Enable Browser Security Features: Avoid installing unknown browser extensions that could compromise your wallet.


    Useful Links and Resources

    • Official Website: Freighter Wallet

    • Documentation: Freighter GitHub Repo

    • Testnet Tokens: How to Get Testnet Tokens

    • Troubleshooting: Troubleshooting & FAQs


    Frequently Asked Questions

    Q: What happens if I lose my recovery phrase?

    • Your recovery phrase is the only way to restore your wallet. If it’s lost, your funds cannot be recovered.

    Q: How do I switch between Testnet and Mainnet?

    1. Open the Freighter extension.

    2. Click on the settings icon.

    3. Toggle between Testnet and Mainnet in the dropdown.

    Rabet Wallet Logo
    ⚙️ How Decentralization Fits In

    Traditional escrows live in someone else’s infrastructure — you trust the platform or a third-party agent to hold and release funds. In Trustless Work, the escrow itself is the infrastructure. Each one is an independent smart contract that holds logic, roles, and balances directly on the Stellar blockchain.

    This design gives you:

    • Transparency — anyone can verify the escrow in real time.

    • Composability — you can plug this logic into your own stack.

    • Control — you decide how much you abstract or automate.


    🧩 The Trustless Work Architecture

    Layer
    Tool
    What It Does
    Typical User

    1. Smart Contract Layer

    Soroban Escrow Contract

    Core logic for milestones, roles, and fund releases.

    Everyone — the foundation

    2. Integration Layer

    Escrow API & SDK

    Programmatic control from your backend or frontend.

    Developers

    3. Interaction Layer

    Back Office dApp

    Visual control panel for creating, funding, and managing escrows.

    Each tool works independently, but connects seamlessly through the same escrow contracts and API logic.


    🧱 Hybrid Implementation Models

    Not every company will deploy the entire stack. That’s why Trustless Work is hybrid by design — you can start manual, add automation later, or plug in your own UI at any time.

    1. Back Office–First

    Launch without writing code. Use the Back Office to deploy escrows, define roles, and manage releases. Then embed escrow status widgets or Viewer links on your own landing pages.

    → Best for pilots, MVPs, or early marketplaces.


    2. Hybrid API + Back Office

    Create escrows through the API (from your app), but handle approvals or disputes in the Back Office. Combine your UX with our governance layer.

    → Best for platforms that want control, without managing every on-chain flow.


    3. Transparency Add-On

    Keep your existing payment flow, but connect your users to the Escrow Viewer for proof-of-funds and progress tracking.

    → Best for compliance-heavy or high-trust environments.


    4. Template Fork

    Fork the Demo dApp or Back Office, rebrand it, integrate your wallet provider or custom logic, and ship fast.

    → Best for startups or teams that want to own the UI but use our underlying logic.


    🌍 Example Hybrid Flow

    Scenario: A freelance marketplace wants to add milestone-based payments.

    • They deploy escrows in the Back Office.

    • Use their own frontend (built in Next.js) to list jobs and show milestone progress.

    • Embed the Escrow Viewer link for each job to give users transparent proof-of-funds.

    • When ready to scale, they integrate the API to automate escrow creation and releases.

    No blockchain devs. No audits. Just composable infrastructure.


    🧠 Why This Architecture Wins

    • Decentralization = Independence You’re not locked into a vendor or a custodial middleman. The contract exists on-chain, and your users can verify it anytime.

    • Hybrid = Speed You can start no-code and move to code later. Back Office today, API tomorrow — same logic, same escrows.

    • Transparency = Trust The Viewer turns every transaction into a live proof-of-funds page. Users don’t have to take your word — they can see the escrow themselves.


    💡 Key Takeaway

    Trustless Work isn’t just an escrow API — it’s an architecture for programmable trust. You can centralize your UX while decentralizing your money flow. You can use our Back Office as your admin layer, the Viewer as your transparency layer, and the API as your automation layer — all connected to the same on-chain contracts.

    You own the experience. The blockchain owns the trust.

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Content-Type

    application/json

    x-api-key

    <token>

    <token>

    <token>

    <token>

    <token>

    <token>

    Content-Type

    application/json

    x-api-key

    <token>

    <token>

    Testnet Tokens

    What Are Testnet Tokens?

    Testnet tokens are virtual assets used on Stellar's test network (testnet) to:

    • Test wallet setups.

    • Experiment with sending and receiving payments.

    • Learn Stellar operations like creating trustlines or setting up smart contracts.

    Why Use Testnet Tokens?

    • Risk-Free Learning: No monetary value; perfect for practice.

    • Development Testing: Test your applications or smart contracts before deploying on the mainnet.

    • Community Access: Many developers and testers rely on the testnet for Stellar experiments.


    How to Obtain XLM Testnet Tokens

    Option 1: Use the Stellar Lab Faucet

    1. Go to the Stellar Laboratory:

    2. Paste your Public Key into the faucet's input field.

    3. Click "Request Lumens":

      • You’ll receive a transaction confirmation.


    Option 2: Use Stellar Command-Line Interface (CLI)

    1. Install the Stellar CLI:

      • Install the stellar-core or stellar-horizon command-line tool (refer to Stellar documentation).

    2. Request Tokens:


    How to Get Testnet USDC

    1. Set the Trustline: Ensure you've established a trustline for USDC on your Stellar testnet account. Issuer: GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5

    2. Visit the Circle USDC Faucet:

      • Go to .

    3. Select Stellar and Request USDC:


    How to Use Testnet Tokens

    1. Explore Wallet Operations

    • Send tokens between test accounts.

    • Experiment with memo fields, transaction fees, and multi-signature setups.

    2. Learn Key Stellar Concepts

    • Trustlines: Use testnet tokens to create trustlines for custom assets.

    • Asset Issuance: Test issuing and trading custom assets.

    • Smart Contracts: Experiment with Stellar's pre-signed transactions to simulate smart contracts.

    3. Connect to Applications

    • Use your testnet wallet with Stellar-based dApps like Trustless Work to test integrations.



    Important Notes

    1. Testnet Tokens Have No Value:

      • These tokens are strictly for testing and cannot be transferred to the mainnet.

    2. Testnet Environment Resets:

      • The Stellar testnet resets periodically, clearing all test accounts and balances.


    Troubleshooting & FAQs

    I didn’t receive test tokens. What should I do?

    • Ensure you’ve used the correct Public Key.

    • Try another method (e.g., Stellar Faucet vs. Laboratory).

    • Wait for a few minutes as the testnet might experience delays.

    Why can’t I switch my wallet to Testnet?

    • Make sure your wallet supports Stellar’s testnet.

    • Check for updates or consider using a wallet that supports testnet, like Freighter or Rabet.

    What happens if I lose my Secret Key?

    • Without the Secret Key, you cannot access your testnet account. Treat it as you would a real wallet key.


    Additional Resources


    By following this guide, you can confidently explore Stellar’s testnet and test a variety of operations risk-free.

    Deploy

    Deploy the escrow contract and define the escrow properties.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    <token>

    Milestone

    Name
    Type
    Description

    Roles:

    Name
    Type
    Description

    Trustline:

    Name
    Type
    Description

    Initialize Escrow

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Deploy

    Deploy the escrow contract and define the escrow properties.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    <token>

    Milestone

    Name
    Type
    Description

    Roles:

    Name
    Type
    Description

    Trustline:

    Name
    Type
    Description

    Initialize Escrow

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Update Escrow

    This endpoint allows you to change the properties of an escrow as long as a series of requirements are met, which will be mentioned in this section.

    Requirements to use:

    1. Only the entity with the platform role has permissions to execute this endpoint

    2. If an escrow has funds, the only thing the platform can do is add more milestones. The other properties cannot be modified under any circumstances.

    Headers

    Name
    Value

    Roles:

    Name
    Type
    Description

    Milestone:

    Name
    Type
    Description

    Trustline

    Name
    Type
    Description

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    Trustless Work API

    What is the Trustless Work API?

    The Trustless Work REST API is a developer interface for creating and managing decentralized escrow contracts on the Stellar blockchain using Soroban smart contracts. It simplifies the escrow lifecycle and integrates seamlessly into any platform that needs conditional payments or trust-minimized fund release.


    🚀 Core Capabilities

    • Deploy Smart Escrows: Initialize smart contracts with defined roles, milestones, and conditions.

    • Fund Escrows: Lock funds into escrow accounts with Stellar-native assets (e.g., USDC).

    • Update & Approve Milestones: Collaborate on progress tracking and delivery verification.

    • Dispute Handling: Programmatically raise or resolve disputes.


    🧩 Escrow Types

    1. Single-Release Escrow

      • One-time fund release after milestone approval or dispute resolution.

      • Roles: Service Provider, Approver, Receiver, Dispute Resolver.

    2. Multi-Release Escrow


    📘 Key API Endpoints (Grouped)

    🔨 Deployment

    • /deployer/single-release

    • /deployer/multi-release

    💸 Funding

    • /escrow/{type}/fund-escrow

    ✅ Milestone Handling

    • /escrow/{type}/approve-milestone

    • /escrow/{type}/change-milestone-status

    🏁 Finalization

    • /escrow/{type}/release-funds (single)

    • /escrow/{type}/release-milestone-funds (multi)

    ⚠️ Disputes

    • /escrow/{type}/dispute-escrow

    • /escrow/{type}/resolve-dispute

    • /escrow/{type}/dispute-milestone (multi)

    🔄 Escrow Updates

    • /escrow/{type}/update-escrow

    📊 Query / Tracking

    • /escrow/get-multiple-escrow-balance

    • /helper/get-escrows-by-signer

    • /helper/get-escrows-by-role


    🧰 Helper Utilities

    • /helper/set-trustline: Set trustline to receive specific tokens like USDC.

    • /helper/send-transaction: Submit signed XDR transactions to Stellar.

    • /helper/get-multiple-escrow-balance: Batch query of escrow balances.


    🛡️ Security & Constraints

    • Unsigned Transactions: All operations return unsigned XDRs requiring client-side signing.

    • Role-Based Permissions: Specific actions (e.g., approve, dispute) require the correct role.

    • Rate Limits: 50 requests/minute per client.

    • Fee Model: A 0.3% mainnet fee is taken by Trustless Work, with platforms able to add their own fee.


    🎯 Use Cases

    • Freelance platforms

    • High-value e-commerce

    • SaaS billing

    • Cross-border real estate


    📌 Dev Resources

    Dev Map:

    • Swagger:

    • GitHub:

    useWithdrawRemainingFunds

    In a multi-release escrow, when some funds are locked, you can use this hook to release the remaining funds

    Usage

    This custom hook exposes a function to do the withdraw remaining funds in an escrow.

    import { useResolveDispute } from "@trustless-work/escrow/hooks";
    import { WithdrawRemainingFundsPayload } from "@trustless-work/escrow/types";
    
    /*
     *  useWithdrawRemainingFunds
    */
    const { withdrawRemainingFunds} = useWithdrawRemainingFunds();
    
    /* 
     * It returns an unsigned transaction
     * payload should be of type `WithdrawRemainingFundsPayload`
    */
    const { unsignedTransaction } = await withdrawRemainingFunds(payload);
    

    Mutation Function

    withdrawRemainingFunds

    Responsible for building and returning an unsigned transaction based on the provided payload.

    EscrowType: Specifies the type of escrow. It accepts the following values:

    • multi-release: Allows for multiple releases of funds.

    WithdrawRemainingFundsPayload: An object with fields necessary to release the locked funds

    Parameters:

    Only allows multi-release escrows..

    • payload: An object containing the required fields to resolve a dispute.

    Return Value:

    unsignedTransaction: An object representing the constructed transaction, ready to be signed by your wallet and broadcast.


    Usage Example

    https://dapp.trustlesswork.comdapp.trustlesswork.com

    Dispute Resolution

    Phase 6 — Dispute Resolution (When Trust Meets Judgment)

    No matter how well-designed a process is, disagreements happen. That’s why every Trustless Work escrow includes a final safeguard: Dispute Resolution.

    This phase ensures that when parties disagree on delivery or results, funds don’t vanish into uncertainty. They stay locked in the escrow until a Dispute Resolver decides where they should go.


    Update Escrow

    This endpoint allows you to change the properties of an escrow as long as a series of requirements are met, which will be mentioned in this section.

    Requirements to use:

    1. Only the entity with the platform role has permissions to execute this endpoint

    2. If an escrow has funds, the only thing the platform can do is add more milestones. The other properties cannot be modified under any circumstances.

    useInitializeEscrow

    Deploy the escrow contract and define the escrow properties.

    Usage

    This custom hook exposes a function to deploy an escrow.

    Function

    deployEscrow

    Getting Started - Blocks

    Overview

    Trustless Work React library is a collection of React hooks and entities. It combines the following packages:

    • — Performant, flexible library for managing forms in React.

    useSendTransaction

    Most Trustless Work endpoints return an unsigned transaction in XDR format. This endpoint is used to sign such unsigned transactions and send them to the Stellar network.

    This endpoint must be used for all the endpoints after we execute it. Except getEscrowBalances, getEscrowsByContractId, getEscrowsByRole and getEscrowsBySigner .

    Usage

    This custom hook exposes a function to send a signed transaction to the network.

    useStartDispute

    Responsible for setting the escrow in dispute state. Changes the value of the escrow's "disputed" flag property to true.

    Usage

    This custom hook exposes a function to start a dispute in an escrow.

    Mutation Function

    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/escrow/single-release/approve-milestone",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/escrow/multi-release/change-milestone-status",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/escrow/single-release/change-milestone-status",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/any-endpoint",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network  <--------------- THIS
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/escrow/single-release/release-funds",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/escrow/multi-release/fund-escrow",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/escrow/single-release/resolve-dispute",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/escrow/multi-release/withdraw-remaining-funds",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.post(
          "/escrow/multi-release/release-milestone-funds",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        // Execute the endpoint
        const response = await http.post(
          "/escrow/single-release/dispute-escrow",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }

    Ops, admins, non-dev teams

    4. Transparency Layer

    Escrow Viewer

    Public, read-only audit tool for contracts on testnet or mainnet.

    Compliance, users, investors

    5. Experimentation Layer

    Demo Lab dApp

    Sandbox for learning and rapid testing.

    Builders, hackathons

    6. Automation Layer

    AI Agents / Webhooks (coming soon)

    Automate milestone checks, approvals, or payouts.

    Advanced users

    Release Funds: Release escrowed amounts only when predefined conditions are fulfilled.

  • Real-Time Status Tracking: Query escrow status, milestones, and balances.

  • Cross-Chain Compatibility: USDC support via Circle’s cross-chain transfer protocol.

  • Multiple milestone-based payouts.

  • Each milestone is independently approved and released.

  • /escrow/{type}/resolve-milestone-dispute (multi)
    Legal & professional services
  • Crowdfunding and grants

  • Security deposits

  • https://www.trustlesswork.com/developers
    https://dev.api.trustlesswork.com/docs
    https://github.com/Trustless-Work
    Logo
    Logo

    Check your wallet to confirm receipt of 10,000 XLM test tokens.

    Run the following command:

  • Replace <Your Public Key> with your Stellar wallet address.

  • Verify Balance:

    • Use the CLI or a Stellar wallet to check your testnet account balance.

  • Use the dropdown menu to select "Stellar".

  • Paste your Stellar testnet address.

  • Click on "Get Tokens" to receive testnet USDC.

  • Always re-create your accounts and re-fund them when the testnet resets.

  • Avoid Sharing Secret Keys:

    • Even in a test environment, keep your Secret Key secure to mimic best practices for the mainnet.

  • Stellar Laboratory
    Circle USDC Faucet
    Stellar Testnet Status
    Stellar Laboratory
    Stellar Developer Documentation
    Stellar Discord Community
    Withdraw Remaining Funds
    Responsible for building and returning an unsigned transaction based on the provided payload.

    EscrowType: Specifies the type of escrow. It accepts the following values:

    • multi-release: Allows for multiple releases of funds.

    • single-release: Funds are released in a single transaction.

    InitializeSingleReleaseEscrowPayload: An object with fields necessary to initialize a single-release escrow.

    InitializeMultiReleaseEscrowPayload: An object with fields necessary to initialize a multi-release escrow.

    Parameters:

    Ensure they match: if you choose a "multi-release" type, you must also use a "multi-release" payload.

    • type: Describes the escrow type to be used. Options are "multi-release" or "single-release".

    • payload: An object containing the required fields to initialize an escrow.

    Return Value:

    unsignedTransaction: An object representing the constructed transaction, ready to be signed by your wallet and broadcast.


    Usage Example

    Deploy
    Function
    • sendTransaction Responsible for building and returning data based on the provided payload.

    Argument:

    payload: An string containing the required fields to send a transaction to the network.

    Return Value:

    For: Fund Escrow, Resolve Dispute, Change Milestone Status, Change Milestone Approved Flag, Start Dispute, Release Funds:

    • This object will be a type of sendTransactionResponse.

    For: Initialize Escrow:

    • This object will be a type of sendTransactionResponse. But you can set it as InitializeEscrowResponse.

    For: Update Escrow:

    • This object will be a type of sendTransactionResponse. But you can set it as UpdateEscrowResponse.


    Usage Example

    Types
    startDispute

    This is the main mutation function. Internally, it wraps mutate or mutateAsync and is responsible for building and returning an unsigned transaction based on the provided payload.

    EscrowType: Specifies the type of escrow. It accepts the following values:

    • multi-release: Allows for multiple releases of funds.

    • single-release: Funds are released in a single transaction.

    SingleReleaseStartDisputePayload: An object with fields necessary to dispute a single-release escrow.

    MultiReleaseStartDisputePayload: An object with fields necessary to dispute a multi-release escrow by milestone.

    Parameters:

    Ensure they match: if you choose a "multi-release" type, you must also use a "multi-release" payload.

    • type: Describes the escrow type to be used. Options are "multi-release" or "single-release".

    • payload: An object containing the required fields to initialize an escrow.

    Return Value:

    unsignedTransaction: An object representing the constructed transaction, ready to be signed by your wallet and broadcast.


    Usage Example

    Start Dispute
    stellar-cli request-tokens --network testnet --public-key <Your Public Key>
    src/hooks/useWithdrawRemainingFundsForm.ts
    import {
      useWithdrawRemainingFunds,
      useSendTransaction,
    } from "@trustless-work/escrow/hooks";
    import {
      WithdrawRemainingFundsPayload
    } from "@trustless-work/escrow/types";
    
    export const useStartDisputeForm = () => {
    
     /*
      *  useWithdrawRemainingFunds
     */
     const { withdrawRemainingFunds } = useWithdrawRemainingFunds();
     
     /*
      *  useSendTransaction
     */
     const { sendTransaction } = useSendTransaction();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: WithdrawRemainingFundsPayload) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the withdrawRemainingFunds function
           * - The result will be an unsigned transaction
           */
          const { unsignedTransaction } = await withdrawRemainingFunds (
            payload
          );
    
          if (!unsignedTransaction) {
            throw new Error(
              "Unsigned transaction is missing from withdrawRemainingFunds."
            );
          }
    
          /**
           * @Note:
           * - We need to sign the transaction using your [private key] such as wallet
           * - The result will be a signed transaction
           */
          const signedXdr = await signTransaction({ /* This method should be provided by the wallet */
            unsignedTransaction,
            address: walletAddress || "",
          });
    
          if (!signedXdr) {
            throw new Error("Signed transaction is missing.");
          }
    
          /**
           * @Note:
           * - We need to send the signed transaction to the API
           * - The data will be an SendTransactionResponse
           */
          const data = await sendTransaction(signedXdr);
    
          /**
           * @Responses:
           * data.status === "SUCCESS"
           * - Dispute resolved successfully
           * - Show a success toast
           *
           * data.status == "ERROR"
           * - Show an error toast
           */
          if (data.status === "SUCCESS") {
             toast.success("Withdrawal successful");
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    import { useInitializeEscrow } from "@trustless-work/escrow/hooks";
    import { InitializeSingleReleaseEscrowPayload, InitializeMultiReleaseEscrowPayload } from "@trustless-work/escrow/types";
    
    /*
     *  useInitializeEscrow 
    */
    const { deployEscrow } = useInitializeEscrow();
    
    /* 
     * It returns an unsigned transaction
     * payload should be of type `InitializeMultiReleaseEscrowPayload` or `InitializeSingleReleaseEscrowPayload`
    */
    const { unsignedTransaction } = await deployEscrow(payload);
    
    src/hooks/useInitializeEscrowForm.ts
    import {
      useInitializeEscrow,
      useSendTransaction,
    } from "@trustless-work/escrow/hooks";
    import {
      InitializeMultiReleaseEscrowPayload,
      InitializeSingleReleaseEscrowPayload
    } from "@trustless-work/escrow/types";
    
    export const useInitializeEscrowForm = () => {
    
     /*
      *  useInitializeEscrow
     */
     const { deployEscrow } = useInitializeEscrow();
     
     /*
      *  useSendTransaction
     */
     const { sendTransaction } = useSendTransaction();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: InitializeSingleReleaseEscrowPayload | InitializeMultiReleaseEscrowPayload) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the deployEscrow function
           * - The result will be an unsigned transaction
           */
          const { unsignedTransaction } = await deployEscrow(
            payload,
            "multi-release"
            // or ...
            // "single-release"
          );
    
          if (!unsignedTransaction) {
            throw new Error(
              "Unsigned transaction is missing from deployEscrow response."
            );
          }
    
          /**
           * @Note:
           * - We need to sign the transaction using your [private key] such as wallet
           * - The result will be a signed transaction
           */
          const signedXdr = await signTransaction({ /* This method should be provided by the wallet */
            unsignedTransaction,
            address: walletAddress || "",
          });
    
          if (!signedXdr) {
            throw new Error("Signed transaction is missing.");
          }
    
          /**
           * @Note:
           * - We need to send the signed transaction to the API
           * - The data will be an SendTransactionResponse
           */
          const data = await sendTransaction(signedXdr);
    
          /**
           * @Responses:
           * data.status === "SUCCESS"
           * - Escrow initialized successfully
           * - Show a success toast
           *
           * data.status == "ERROR"
           * - Show an error toast
           */
          if (data.status === "SUCCESS") {
            toast.success("Escrow Created");
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    import { useSendTransaction} from "@trustless-work/escrow/hooks";
    
    /*
     *  useSendTransaction
    */
    const { sendTransaction } = useSendTransaction();
    
    /* 
     * It returns a SendTransactionResponse
     * payload should be of type string
    */
    const data = await sendTransaction(signedXdr);
    
    src/hooks/useSendTransactionForm.ts
    import {
      useFundEscrow,
      useSendTransaction,
    } from "@trustless-work/escrow/hooks";
    import {
      useSomeEndpointPayload
    } from "@trustless-work/escrow/types";
    
    export const useSomeEndpointForm= () => {
    
     /*
      *  useSomeEndpoint
     */
     const { someFunction } = useSomeEndpoint();
     
     /*
      *  useSendTransaction
     */
     const { sendTransaction } = useSendTransaction();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: useSomeEndpointPayload) => {
    
        try {
          // get unsignedTransaction from some endpoint ...
    
          /**
           * @Note:
           * - We need to sign the transaction using your [private key] such as wallet
           * - The result will be a signed transaction
           */
          const signedXdr = await signTransaction({ /* This method should be provided by the wallet */
            unsignedTransaction,
            address: walletAddress || "",
          });
    
          if (!signedXdr) {
            throw new Error("Signed transaction is missing.");
          }
    
          /**
           * @Note:
           * - We need to send the signed transaction to the API
           * - The data will be an SendTransactionResponse
           */
          const data = await sendTransaction(signedXdr);
    
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    import { useStartDispute } from "@trustless-work/escrow/hooks";
    import { SingleReleaseStartDisputePayload, MultiReleaseStartDisputePayload } from "@trustless-work/escrow/types";
    
    /*
     *  useStartDispute
    */
    const { startDispute } = useStartDispute();
    
    /* 
     * It returns an unsigned transaction
     * payload should be of type `MultiReleaseStartDisputePayload` or `SingleReleaseStartDisputePayload`
    */
    const { unsignedTransaction } = await startDispute(payload);
    
    src/hooks/useStartDisputeForm.ts
    import {
      useStartDispute,
      useSendTransaction,
    } from "@trustless-work/escrow/hooks";
    import {
      SingleReleaseStartDisputePayload, MultiReleaseStartDisputePayload
    } from "@trustless-work/escrow/types";
    
    export const useStartDisputeForm = () => {
    
     /*
      *  useStartDispute
     */
     const { startDispute } = useStartDispute();
     
     /*
      *  useSendTransaction
     */
     const { sendTransaction } = useSendTransaction();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: SingleReleaseStartDisputePayload | MultiReleaseStartDisputePayload) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the startDispute function
           * - The result will be an unsigned transaction
           */
          const { unsignedTransaction } = await startDispute(
            payload,
            "multi-release"
            // or ...
            //"single-release"
          );
    
          if (!unsignedTransaction) {
            throw new Error(
              "Unsigned transaction is missing from startDispute."
            );
          }
    
          /**
           * @Note:
           * - We need to sign the transaction using your [private key] such as wallet
           * - The result will be a signed transaction
           */
          const signedXdr = await signTransaction({ /* This method should be provided by the wallet */
            unsignedTransaction,
            address: walletAddress || "",
          });
    
          if (!signedXdr) {
            throw new Error("Signed transaction is missing.");
          }
    
          /**
           * @Note:
           * - We need to send the signed transaction to the API
           * - The data will be an SendTransactionResponse
           */
          const data = await sendTransaction(signedXdr);
    
          /**
           * @Responses:
           * data.status === "SUCCESS"
           * - Dispute started successfully
           * - Show a success toast
           *
           * data.status == "ERROR"
           * - Show an error toast
           */
          if (data.status === "SUCCESS") {
             toast.success("Dispute Started");
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    

    disputeResolver

    string

    Address in charge of resolving disputes within the escrow.

    receiver

    string

    Address where escrow proceeds will be sent to

    description

    string

    Text describing the function of the milestone

    status

    string (Default value: "peding")

    Milestone status. Ex: Approved, In dispute, etc...

    approved

    boolean (Default value: false)

    Flag indicating whether a milestone has been approved by the approver

    approver

    string

    Address of the entity requiring the service.

    serviceProvider

    string

    Address of the entity providing the service.

    plataformAddress

    string

    Address of the entity that owns the escrow

    releaseSigner

    string

    Address of the user in charge of releasing the escrow funds to the service provider.

    address

    string

    Public address establishing permission to accept and use a specific token.

    symbol

    string

    Official abbreviation representing the token in wallets, exchanges, and documentation.

    receiver

    string

    Address where escrow proceeds will be sent to

    disputeResolver

    string

    Address in charge of resolving disputes within the escrow.

    description

    string

    Text describing the function of the milestone

    status

    string (Default value: "peding")

    Milestone status. Ex: Approved, In dispute, etc...

    approved

    boolean (Default value: false)

    Flag indicating whether a milestone has been approved by the approver

    amount

    string

    Amount of the milestone

    approver

    string

    Address of the entity requiring the service.

    serviceProvider

    string

    Address of the entity providing the service.

    plataformAddress

    string

    Address of the entity that owns the escrow

    releaseSigner

    string

    Address of the user in charge of releasing the escrow funds to the service provider.

    address

    string

    Public address establishing permission to accept and use a specific token.

    symbol

    string

    Official abbreviation representing the token in wallets, exchanges, and documentation.

    disputeResolver

    string

    Address in charge of resolving disputes within the escrow.

    receiver

    string

    Address where escrow proceeds will be sent to

    Content-Type

    application/json

    x-api-key

    <token>

    approver

    string

    Address of the entity requiring the service.

    serviceProvider

    string

    Address of the entity providing the service.

    platformAddress

    string

    Address of the entity that owns the escrow

    releaseSigner

    string

    Address of the user in charge of releasing the escrow funds to the service provider.

    description

    string

    Text describing the function of the milestone.

    status

    string

    Milestone status. Ex: Approved, In dispute, etc...

    amount

    boolean

    Amount of the milestone

    address

    string

    Public address establishing permission to accept and use a specific token.

    ⚖️ The Role of the Dispute Resolver

    The Dispute Resolver is the only address authorized to intervene once a dispute is raised. This role represents a neutral authority — it can be:

    • A platform’s customer support team mediating between users

    • A DAO-based arbitration module

    • A trusted third party or auditor

    • Or, in advanced setups, a decentralized dispute resolution DAO

    The resolver’s job is to review both sides, look at the evidence, and decide how the locked funds will be distributed.


    🧾 How a Dispute Is Raised

    Disputes can be triggered by either:

    • The Service Provider (e.g., claiming they delivered as promised), or

    • The Approver (e.g., claiming the work was unsatisfactory).

    Once raised:

    • The milestone or escrow’s disputed flag is set to true.

    • The contract enters a locked state — meaning no further releases can happen until it’s resolved.

    • All updates and evidence remain visible on-chain for transparency.


    🧠 How the Resolver Makes a Decision

    The Dispute Resolver signs a resolution transaction that includes:

    1. A list of addresses and amounts to re-route the funds to.

    2. Optional evidence or reasoning (usually an off-chain link or case reference).

    💡 This flexible format replaces the older binary system (refund or payout). It allows more nuanced outcomes — partial refunds, multi-party settlements, or even new allocations in special cases (like shared credit lines or pooled contributions).


    🔄 Transparency and Traceability

    Every resolution is publicly verifiable and immutable:

    • The contract emits a Resolution Event containing all the distributions.

    • These amounts become part of the escrow’s historical record.

    • Anyone can inspect who received what, when, and why.

    This creates transparent, tamper-proof accountability — essential for platforms that need audit trails, regulatory compliance, or internal oversight.

    You can verify resolution details directly in:

    • Escrow Viewer — structured breakdown of resolution outcomes

    • Stellar Expert — raw transaction data and event logs


    👥 Who Plays This Role in Practice

    Depending on the ecosystem, the Dispute Resolver can be implemented in different ways:

    Scenario
    Dispute Resolver
    Example

    Marketplace or SaaS Platform

    Platform’s customer support team

    Upwork, Fiverr-style review desk

    Grants or DAOs

    Governance contract or arbitration module

    Community voting or delegated resolution

    Private Credit & Finance

    Escrow manager or legal agent

    Adjusts amounts between borrower, lender, guarantor

    P2P / Trust-Minimized Systems

    Decentralized arbitration

    🧩 The role is flexible — the key is that the resolver’s actions are traceable, transparent, and signed.


    🪶 Evidence and Off-Chain Storage

    Resolvers can attach evidence to their decisions — for example:

    • Case reports

    • Proof of refund agreements

    • Links to decentralized storage (IPFS, Arweave, Filecoin)

    Trustless Work stores only the reference (the URL or hash), not the file itself. This keeps the on-chain data light while preserving a full trail of proof.


    📦 Outcome of the Dispute Resolution Phase

    By the end of this phase:

    • The Dispute Resolver has signed and submitted a resolution transaction.

    • Funds have been re-routed according to the distribution list.

    • The escrow’s resolved flag is set to true.

    • All movements are publicly visible and auditable.

    💡 The Dispute Phase proves that even in disagreement, trust can remain programmable. No hidden decisions — every outcome is on-chain, traceable, and final.

    Headers

    Name
    Value

    Content-Type

    application/json

    x-api-key

    <token>

    Roles:

    Name
    Type
    Description

    approver

    string

    Address of the entity requiring the service.

    serviceProvider

    string

    Address of the entity providing the service.

    platformAddress

    string

    Address of the entity that owns the escrow

    releaseSigner

    string

    Address of the user in charge of releasing the escrow funds to the service provider.

    Milestone:

    Name
    Type
    Description

    description

    string

    Text describing the function of the milestone.

    status

    string

    Milestone status. Ex: Approved, In dispute, etc...

    approved

    boolean

    Flag indicating whether a milestone has been approved by the approver.

    Flags

    Name
    Type
    Description

    disputed

    boolean

    Flag indicating that an escrow is in dispute.

    released

    boolean

    Flag indicating that escrow funds have already been released.

    resolved

    boolean

    Flag indicating that a disputed escrow has already been resolved.

    Trustline

    Name
    Type
    Description

    address

    string

    Public address establishing permission to accept and use a specific token.

    Open API

    What this Endpoint returns?

    This endpoint returns the transaction unsigned so that the transaction can be signed by means of a customer wallet.

    Use Example:

    zod — TypeScript-first schema validation library.
  • @trustless-work/escrow — SDK for handling escrow logic in decentralized apps.

  • @tanstack/react-query — Data-fetching and caching library for React.

  • @tanstack/react-query-devtools — Developer tools for inspecting React Query state.

  • @hookform/resolvers — Resolvers for integrating validation libraries (like Zod) with React Hook Form.

  • @creit.tech/stellar-wallets-kit — Wallet connection toolkit for Stellar blockchain.

  • axios — Promise-based HTTP client for making API requests.

  • @tanstack/react-table — Headless table library for building flexible data grids.

  • react-day-picker — Lightweight date picker component for React.

  • recharts — Charting library built with React and D3.


  • Links

    Setup

    1

    Installation

    Start by installing Trustless Work Blocks

    2

    Initialize Configuration

    Setup your project with blocks

    3

    Configure Environment

    The next step is to configure the Trustless Work provider. You need to configure the following:

    • baseURL: Trustless Work URL, this should be the main or development environment. We provide

    4

    Wrap your App with Providers

    Absolutely must be used: ReactQueryClientProvider | TrustlessWorkProvider | WalletProvider.

    If you want to use some blocks, you should wrap your app with their providers. See more in:

    5

    Add your First Component

    Add wallet connectivity to your app:

    Example usage in a page:

    Now, you are able to interact with Trustless Work blocks.

    react-hook-form

    Entities

    Escrow

    Contains both single-release and multiple-release types.

    Milestone

    Contains both single-release and multiple-release types. Both of them based on the BaseMilestone.

    Trustline

    Flags

    All the possible flags only in multi-release escrow.

    Roles

    Escrow Blocks

    A production-ready set of React blocks for integrating Trustless Work's escrow and dispute resolution flows.

    Would you like to customize the blocks? You can do that by editing the blocks as you see fit.


    What you get

    • UI blocks (cards/tables/dialogs/forms) to list and manage escrows

    • Providers for API config, wallet context, dialogs and amounts

    • TanStack Query hooks for fetching and mutating escrows

    • Wallet-kit helpers and error handling utilities

    Links

    List all available blocks#

    With the CLI you can list all available blocks:

    The init command will:

    • Show all available blocks.

    Context API#

    The context API is a global storage of escrows. It is used to store the escrows that are fetched from the API. It is also used to store the selected escrow.

    Important

    If you don't want to use our approach for retrieving the escrow data, you are completely free to change it. You can use Redux, Zustand, or any other solution instead. However, it is important that you ensure the desired escrow is passed to the endpoint.

    Understanding how the context works in escrows endpoints.#

    When implementing the endpoints, we need to pass the data of a specific escrow to each endpoint. But how do we do that? Our library provides a context called EscrowContext, which includes some very important utilities. Among them areselectedEscrowand setSelectedEscrow, which allow us to do the following:

    Use of selectedEscrow#

    Currently, selectedEscrow holds a specific escrow that we are pointing to. With this, all the endpoint hooks interact with that state in order to extract data from it, such as contractId, roles, etc. For example, in the change status select, the milestoneIndex values are loaded based on the currently selected escrow. Therefore, ifsetSelectedEscrow is undefined, they won't load.

    /useChangeMilestoneStatus.ts

    Use of setSelectedEscrow#

    The function setSelectedEscrow save the selected escrow in the context, so that all the endpoint hooks interact with that state in order to extract data from it, such as contractId, roles, etc. For example, in escrows cards by signer we save the selected escrow in the context, so that we can use it in details dialog.

    /EscrowsCards.tsx

    Use of updateEscrow#

    Our updateEscrow function update the existing selectedEscrow in the context. It is useful to update a flag or others fields. For example, we use it to update the escrow status after a change milestone status mutation.

    /useChangeMilestoneStatus.ts

    Installation based on folder path#

    If you need all the child blocks, you can install them by pointing to their parent directory, so you won't have to install them one by one.

    Install Parent Directory

    Installs ALL escrow blocks

    Install Specific Subfolder

    Installs only single-release escrow blocks

    💡 Pro Tip: Hierarchical Installation

    The deeper you go in the folder structure, the more specific the blocks become. Start with parent directories for comprehensive functionality, then drill down to specific components as needed.

    Initiation Phase

    The Initiation Phase is where the escrow takes shape.

    You’re not moving money yet — you’re defining the logic that will govern how it moves later.

    Think of this as the architecture of trust: setting the rules, actors, and conditions before anything hits the chain.

    Participants and Roles

    In the initiation phase key roles are assigned to specific parties. These roles determine responsibilities and actions throughout the transaction.


    🎭 Define the Roles

    Every escrow is role-based — meaning only specific addresses can perform specific actions.

    You can .

    During initiation, you assign which addresses will act as:

    • Milestone Marker (Service Provider) — delivers work and marks milestones as done

    • Approver — validates each milestone and can raise disputes

    • Release Signer — triggers the release of funds once conditions are met

    • Dispute Resolver — resolves conflicts and reallocates funds

    🔑 Roles are permissions, not identities.

    The same wallet can hold more than one role, depending on your workflow.


    💰 Decide the Amounts and Milestones

    This is where you define what gets paid, and when.

    • For a Single-Release escrow, you’ll have one total amount and one receiver.

      • The payment only happens once, after all milestones are approved.

      • Example: a one-off design project or security deposit.

    • For a

    This structure allows you to fund once and pay multiple parties or stages over time.

    💡 You can even add milestones later — turning one escrow into an ongoing contract.


    🪙 Select the Trustline (Asset Configuration)

    On Stellar, every token (like USDC) is identified by its issuer address.

    To hold that token, your wallet must explicitly “trust” that issuer — this is called a Trustline.

    • When you create an escrow, you must define which trustline (asset) it will hold.

      Example: GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5 = USDC.

    • All participating addresses (Approver, Marker, Release Signer, etc.) must have that trustline enabled in their wallet.

      Otherwise, they won’t be able to receive or send that asset.

    ⚠️ Without the trustline set, the transaction will fail — so ensure every role wallet is ready before deployment.


    🧾 Assign the Engagement ID (Reference Tracking)

    Every escrow includes an Engagement ID, which acts like your external reference number.

    It’s a human-readable tag that connects the on-chain escrow to your off-chain logic.

    Examples:

    • ORDER_2025_00234

    • INVOICE_98B-13

    • DAO_GRANT_ROUND2

    The Engagement ID is optional for blockchain logic, but essential for indexing and analytics.

    It lets platforms query, group, and monitor escrows easily through the API or the viewer.


    ⚙️ Configure Platform Fee

    Platforms can earn a Platform Fee on each escrow.

    This fee is taken at release, alongside the protocol’s 0.3% Trustless Work fee.

    • Example:

      • Platform Fee = 1%

      • Trustless Fee = 0.3%

      • Total deduction = 1.3% (automatically split between platform and protocol)

    The platform fee is sent to the Platform Address defined in the roles.

    💡 For marketplaces and SaaS platforms, this is a native monetization layer built into the escrow logic — no separate billing flow required.


    📦 Output of the Initiation Phase

    At the end of Initiation, you have:

    • A complete schema defining every role, milestone, fee, and asset

    • A trustline selected and validated for all participants

    • An engagement ID linking your escrow to external records

    • A clear understanding of what needs to happen before any money moves

    This is the blueprint.

    Once finalized, it’s deployed to the blockchain as an immutable contract.

    From here on, every signature, approval, or release event happens on-chain.

    Also, you should be able to view the escrow and it’s configuration on the escrow viewer or on Stellar expert.


    Getting Started - SDK

    Overview

    Trustless Work React library is a collection of React hooks and entities. It combines the following packages:

    • Axios for https requests.


    Links

    Setup

    1

    Installation

    Start by installing Trustless Work React Library.

    2

    Important: This library offers seamless integration and allows flexibility with single-release or multi-release escrow options. Simply use the appropriate type parameter and required payload for your needs

    useGetMultipleEscrowBalances

    Returns all the information of an escrow requested through the contractId.

    Usage

    This custom hook exposes a function to get the balances that you are looking obtain.

    import { useGetMultipleEscrowBalances } from "@trustless-work/escrow/hooks";
    import { GetBalanceParams } from "@trustless-work/escrow/types";
    
    /*
     *  useGetEscrow
    */
    const { getMultipleBalances } = useGetMultipleEscrowBalances();
    
    /* 
     * It returns the balances of the escrows
     * payload should be of type `GetBalanceParams`
    */
    await getMultipleBalances(payload);

    Function

    • getMultipleBalances Responsible for building and returning data based on the provided payload.

    Argument:

    GetBalanceParams : An object containing the required fields to get the balances.

    Return Value:

    balances: The balances that you are looking for.


    Usage ExampleForm

    Swagger UIapi.trustlesswork.com
    Swagger UIdev.api.trustlesswork.com
    Trustless WorkGitHub

    Deploy

    Single Release

    Multi Release

    Escrow Lifecycle

    The escrow lifecycle is the structured flow of actions and responsibilities that secure a transaction. At Trustless Work, we break this into clear phases, ensuring transparency, adaptability, and cons

    Core Phases:

    1. Initiation Phase:

    The foundation of the escrow:

    useUpdateEscrow

    This endpoint allows you to change the properties of an escrow as long as a series of requirements are met, which will be mentioned in this section.

    Usage

    This custom hook exposes a function to update an escrow.

    Function

    useChangeMilestoneStatus

    Responsible for modifying the "status" property of a specific milestone in the escrow.

    Usage

    This custom hook exposes a function to change a custom status the milestone.

    Mutation Function

    useGetEscrowsFromIndexerByRole

    Returns the escrows that you're looking for. It comes from our indexer (database) synchronizer with the blockchain.

    Usage

    This custom hook exposes a function to get the escrows that you are looking obtain.

    Function

    Roles in Trustless Work

    Let's understand what each role represents!

    Anyone can deposit funds into an escrow. But only addresses with assigned roles can update milestones, approve work, release funds, or resolve disputes.



    Escrow Roles and Their Actions

    https://stablecoin.stellarlight.xyz/stablecoin.stellarlight.xyz
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        // Execute the endpoint
        const response = await http.post(
          "/deployer/single-release",
          {
            // body requested for the endpoint
          },
        );
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data; 
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        // Execute the endpoint
        const response = await http.post(
          "/deployer/multi-release",
          {
            // body requested for the endpoint
          },
        );
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data; 
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.put(
          "/escrow/multi-release/update-escrow",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    import axios from "axios";
    
    const http = axios.create({
      baseURL: "https://dev.api.trustlesswork.com",
      timeout: 10000,
      headers: {
        "Content-Type": "application/json",
        "x-api-key": your_api_key,
      },
    });
    
    export const useExample = async () => {
        // Get the signer address
        const { address } = await kit.getAddress();
    
        const response = await http.put(
          "/escrow/single-release/update-escrow",
          {
            // body requested for the endpoint
          },
        ); 
        
        // Get the unsigned transaction hash
        const { unsignedTransaction } = response.data;
    
        // Sign the transaction by wallet
        const { signedTxXdr } = await signTransaction(unsignedTransaction, {
          address,
          networkPassphrase: WalletNetwork.TESTNET,
        });
    
        // Send the transaction to Stellar Network
        const tx = await http.post("/helper/send-transaction", {
          signedXdr: signedTxXdr,
        });
    
        const { data } = tx;
    
        return data;
    }
    npm install @trustless-work/blocks
    yarn add @trustless-work/blocks
    /**
     * Single Release Escrow
     */
    export type SingleReleaseEscrow = {
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Unique identifier for the escrow
       */
      engagementId: string;
    
      /**
       * Name of the escrow
       */
      title: string;
    
      /**
       * Roles that make up the escrow structure
       */
      roles: Roles;
    
      /**
       * Text describing the function of the escrow
       */
      description: string;
    
      /**
       * Amount to be transferred upon completion of escrow milestones
       */
      amount: number;
    
      /**
       * Commission that the platform will receive when the escrow is completed
       */
      platformFee: number;
    
      /**
       * Amount of the token (XLM, USDC, EURC, etc) in the smart contract.
       */
      balance: number;
    
      /**
       * Objectives to be completed to define the escrow as completed
       */
      milestones: SingleReleaseMilestone[];
    
      /**
       * Flags validating certain escrow life states
       */
      flags?: Flags;
    
      /**
       * Information on the trustline that will manage the movement of funds in escrow
       */
      trustline: Trustline;
    };
    
    /**
     * Multi Release Escrow
     */
    export type MultiReleaseEscrow = Omit<
      SingleReleaseEscrow,
      "milestones" | "flags" | "amount" | "roles"
    > & {
      milestones: MultiReleaseMilestone[];
      roles: Omit<Roles, "receiver">;
    };
    /**
     * Milestone
     */
    type BaseMilestone = {
      /**
       * Text describing the function of the milestone.
       */
      description: string;
    
      /**
       * Milestone status. Ex: Approved, In dispute, etc...
       */
      status?: string;
    
      /**
       * Evidence of work performed by the service provider.
       */
      evidence?: string;
    };
    
    /**
     * Single Release Milestone
     */
    export type SingleReleaseMilestone = BaseMilestone & {
      /**
       * Approved flag, only if the escrow is single-release
       */
      approved?: boolean;
    };
    
    /**
     * Multi Release Milestone
     */
    export type MultiReleaseMilestone = BaseMilestone & {
      /**
       * Amount to be transferred upon completion of this milestone
       */
      amount: number;
      
      /**
       * Address where milestone proceeds will be sent to
       */
      receiver: string;
    
      /**
       * Flags validating certain milestone life states, only if the escrow is multi-release
       */
      flags?: Flags;
    };

    Uses smart contracts or on-chain juries

    1. Service Provider

    Purpose: Delivers the product, service, or outcome defined in the escrow. Can perform:

    • Change milestone status

    • Add evidence or proof of delivery

    • Raise a dispute

    Examples:

    • Freelancer delivering work and marking it as done

    • Company updating crowdfunding milestones

    • Compliance team marking a “withdrawal check” milestone complete


    2. Approver

    Purpose: Validates that the milestone has indeed been completed and signs the approval. Can perform:

    • Sign the approval of a milestone

    • Raise a dispute if work is not satisfactory

    Examples:

    • Buyer approving a freelancer’s deliverable

    • Host approving a checkout in a rental deposit

    • Platform approving milestones in a crowdfunding campaign


    3. Release Signer

    Purpose: Triggers the actual release of funds once approvals are in place. Can perform:

    • Release funds after all milestones are approved (Single-Release)

    • Release funds for each approved milestone (Multi-Release)

    • Can raise a dispute if there’s disagreement at release stage.

    Examples:

    • Airbnb releasing a deposit to the host

    • DAO releasing a bounty payment to a contributor


    4. Receiver (Final Recipient)

    Purpose: The end destination of funds. Can perform:

    • Receive funds once release is triggered

    Examples:

    • Freelancer wallet receiving payment

    • Company receiving milestone-based funding

    • Tourist receiving their deposit back


    5. Dispute Resolver

    Purpose: Steps in when parties disagree. Can perform:

    • Resolve disputes by redirecting funds

    Examples:

    • Platform deciding how to split a disputed deposit

    • Arbitrator updating milestone pricing in a project

    • Escrow canceled and funds returned to buyer


    6. Platform Address

    Purpose: Represents the platform itself. Can perform:

    • Collect platform fees automatically

    • Update escrow details while escrow has not been funded

    Examples:

    • Airbnb collecting service fees

    • Crowdfunding platform applying a percentage fee

    • Marketplace updating a milestone description before it’s funded


    🧭 How Roles Interact

    1. Service Provider marks milestones as complete

    2. Approver validates or disputes them

    3. Release Signer authorizes payout

    4. Receiver gets funds

    5. Platform Address takes its fee

    6. If there’s a conflict, the Dispute Resolver steps in

    📎 See it in action: Escrow Lifecycle

    Roles are marked in black

    Receiver — the final destination of funds

  • Platform Address — the address of the platform itself (receives a percentage fee and can adjust configuration before funding)

  • Multi-Release escrow
    , you’ll define
    multiple milestones
    , each with:
    • Its own amount

    • Its own receiver

    • Its own flags and status

    read more about roles here → Roles in Trustless Work
    /**
     * Trustline
     */
    export interface Trustline {
      /**
       * Public address establishing permission to accept and use a specific token.
       */
      address: string;
    }
    Get Balances

    Update Escrow

    Single Release

    Multi Release

    updateEscrow

    Responsible for building and returning an unsigned transaction based on the provided payload.

    EscrowType: Specifies the type of escrow. It accepts the following values:

    • multi-release: Allows for multiple releases of funds.

    • single-release: Funds are released in a single transaction.

    UpdateSingleReleaseEscrowPayload: An object with fields necessary to update a single-release escrow.

    UpdateMultiReleaseEscrowPayload: An object with fields necessary to update a multi-release escrow.

    Parameters:

    Ensure they match: if you choose a "multi-release" type, you must also use a "multi-release" payload.

    • type: Describes the escrow type to be used. Options are "multi-release" or "single-release".

    • payload: An object containing the required fields to update an escrow.

    Return Value:

    unsignedTransaction: An object representing the constructed transaction, ready to be signed by your wallet and broadcast.


    Usage Example

    Update Escrow
    changeMilestoneStatus

    Returning an unsigned transaction based on the provided payload.

    EscrowType: Specifies the type of escrow. It accepts the following values:

    • multi-release: Allows for multiple releases of funds.

    • single-release: Funds are released in a single transaction.

    ChangeMilestoneStatusPayload: An object with fields necessary to change the milestone status. It is applicable for both single-release and multi-release escrow types.

    Parameters:

    Ensure they match: if you choose a "multi-release" type, you must also use a "multi-release" payload.

    • type: Describes the escrow type to be used. Options are "multi-release" or "single-release".

    • payload: Contains the data required for change milestone status.

    Return Value:

    unsignedTransaction: An object representing the constructed transaction, ready to be signed by your wallet and broadcast.


    Usage Example

    Change Milestone Status
  • getEscrowsByRole Responsible for building and returning data based on the provided payload.

  • Argument:

    GetEscrowsFromIndexerByRoleParams: An object containing the required fields to get the escrows by role.

    Return Value:

    escrows: The escrows that you are looking for.


    Usage ExampleForm

    import { useGetEscrowsFromIndexerByRole } from "@trustless-work/escrow/hooks";
    import { GetEscrowsFromIndexerByRoleParams } from "@trustless-work/escrow/types";
    
    /*
     *  useGetEscrowsFromIndexerByRole 
    */
    const { getEscrowsByRole } = useGetEscrowsFromIndexerByRole();
    
    /* 
     * It returns the escrows that you are looking for
     * payload should be of type `GetEscrowsFromIndexerByRoleParams`
    */
    await getEscrowsByRole(payload);
    Get Escrows by Role
    /**
     * Flags
     */
    export type Flags = {
      /**
       * Flag indicating that an escrow is in dispute.
       */
      disputed?: boolean;
    
      /**
       * Flag indicating that escrow funds have already been released.
       */
      released?: boolean;
    
      /**
       * Flag indicating that a disputed escrow has already been resolved.
       */
      resolved?: boolean;
    
      /**
       * Flag indicating whether a milestone has been approved by the approver.
       */
      approved?: boolean;
    };
    
    /**
     * Roles
     */
    export type Roles = {
      /**
       * Address of the entity requiring the service.
       */
      approver: string;
    
      /**
       * Address of the entity providing the service.
       */
      serviceProvider: string;
    
      /**
       * Address of the entity that owns the escrow
       */
      platformAddress: string;
    
      /**
       * Address of the user in charge of releasing the escrow funds to the service provider.
       */
      releaseSigner: string;
    
      /**
       * Address in charge of resolving disputes within the escrow.
       */
      disputeResolver: string;
    
      /**
       * Address where escrow proceeds will be sent to (In the “Multi-Release” version, 
         this role is at the milestone level.)
       */
      receiver: string;
    };
    
    /**
     * Role
     */
    export type Role =
      | "approver"
      | "serviceProvider"
      | "platformAddress"
      | "releaseSigner"
      | "disputeResolver"
      | "receiver"
      | "signer";
    src/hooks/useGetMultipleEscrowBalances.ts
    import {
      useGetMultipleEscrowBalances,
    } from "@trustless-work/escrow/hooks";
    import {
      GetBalanceParams, 
    } from "@trustless-work/escrow/types";
    
    export const useGetMultipleEscrowBalancesForm = () => {
    
     /*
      *  useGetMultipleEscrowBalances
     */
     const { getMultipleBalances } = useGetMultipleEscrowBalances();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: GetBalanceParams) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the getMultipleBalances function
           * - The result will be balances
          */
          const { balances } = await getMultipleBalances(payload);
          
          if (!balances) {
            throw new Error("Balances not found");
          }
    
          /**
           * @Responses:
           * balances !== null
           * - Balances received successfully
           * - Show a success toast
           *
           * balances === null
           * - Show an error toast
           */
          if (balances) {
            toast.success("Balances Received");
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    /**
     * Single Release Initialize Escrow Payload
     */
    export type InitializeSingleReleaseEscrowPayload = {
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    
      /**
       * Unique identifier for the escrow
       */
      engagementId: string;
    
      /**
       * Name of the escrow
       */
      title: string;
    
      /**
       * Roles that make up the escrow structure
       */
      roles: {
        /**
         * Address of the entity requiring the service.
         */
        approver: string;
    
        /**
         * Address of the entity providing the service.
         */
        serviceProvider: string;
    
        /**
         * Address of the entity that owns the escrow
         */
        platformAddress: string;
    
        /**
         * Address of the user in charge of releasing the escrow funds to the service provider.
         */
        releaseSigner: string;
    
        /**
         * Address in charge of resolving disputes within the escrow.
         */
        disputeResolver: string;
    
        /**
         * Address where escrow proceeds will be sent to
         */
        receiver: string;
      };
    
      /**
       * Text describing the function of the escrow
       */
      description: string;
    
      /**
       * Amount to be transferred upon completion of escrow milestones
       */
      amount: number;
    
      /**
       * Commission that the platform will receive when the escrow is completed
       */
      platformFee: number;
    
      /**
       * Flags validating certain escrow life states
       */
      flags?: {
        /**
         * Flag indicating that an escrow is in dispute.
         */
        disputed?: boolean;
    
        /**
         * Flag indicating that escrow funds have already been released.
         */
        released?: boolean;
    
        /**
         * Flag indicating that a disputed escrow has already been resolved.
         */
        resolved?: boolean;
    
        /**
         * Flag indicating whether a milestone has been approved by the approver.
         */
        approved?: boolean;
      };
    
      /**
       * Information on the trustline that will manage the movement of funds in escrow
       */
      trustline: {
        /**
         * Public address establishing permission to accept and use a specific token.
         */
        address: string;
        
        /**
         * Official abbreviation representing the token in wallets, exchanges, and documentation.
         */
        symbol: string;
      };
    
      /**
       * Objectives to be completed to define the escrow as completed
       */
      milestones: {
        /**
         * Text describing the function of the milestone
         */
        description: string;
      }[];
    };
    /**
     * Multi Release Initialize Escrow Payload
     */
    export type InitializeMultiReleaseEscrowPayload = {
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    
      /**
       * Unique identifier for the escrow
       */
      engagementId: string;
    
      /**
       * Name of the escrow
       */
      title: string;
    
      /**
       * Roles that make up the escrow structure (without receiver, as each milestone has its own receiver)
       */
      roles: {
        /**
         * Address of the entity requiring the service.
         */
        approver: string;
    
        /**
         * Address of the entity providing the service.
         */
        serviceProvider: string;
    
        /**
         * Address of the entity that owns the escrow
         */
        platformAddress: string;
    
        /**
         * Address of the user in charge of releasing the escrow funds to the service provider.
         */
        releaseSigner: string;
    
        /**
         * Address in charge of resolving disputes within the escrow.
         */
        disputeResolver: string;
      };
    
      /**
       * Text describing the function of the escrow
       */
      description: string;
    
      /**
       * Commission that the platform will receive when the escrow is completed
       */
      platformFee: number;
    
      /**
       * Information on the trustline that will manage the movement of funds in escrow
       */
      trustline: {
        /**
         * Public address establishing permission to accept and use a specific token.
         */
        address: string;
        
        /**
         * Official abbreviation representing the token in wallets, exchanges, and documentation.
         */
        symbol: string;
      };
    
      /**
       * Objectives to be completed to define the escrow as completed
       */
      milestones: {
        /**
         * Text describing the function of the milestone
         */
        description: string;
        /**
         * Amount to be transferred upon completion of this milestone
         */
        amount: number;
        /**
         * Address where milestone proceeds will be sent to
         */
        receiver: string;
      }[];
    };
    /**
     * Single Release Update Escrow Payload
     */
    export type UpdateSingleReleaseEscrowPayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Escrow data
       */
      escrow: {
        /**
       * Unique identifier for the escrow
       */
        engagementId: string;
    
        /**
         * Name of the escrow
         */
        title: string;
    
        /**
         * Roles that make up the escrow structure
         */
        roles: {
          /**
           * Address of the entity requiring the service.
           */
          approver: string;
    
          /**
           * Address of the entity providing the service.
           */
          serviceProvider: string;
    
          /**
           * Address of the entity that owns the escrow
           */
          platformAddress: string;
    
          /**
           * Address of the user in charge of releasing the escrow funds to the service provider.
           */
          releaseSigner: string;
    
          /**
           * Address in charge of resolving disputes within the escrow.
           */
          disputeResolver: string;
    
          /**
           * Address where escrow proceeds will be sent to
           */
          receiver: string;
        };
    
        /**
         * Text describing the function of the escrow
         */
        description: string;
    
        /**
         * Amount to be transferred upon completion of escrow milestones
         */
        amount: number;
    
        /**
         * Commission that the platform will receive when the escrow is completed
         */
        platformFee: number;
    
        /**
         * Objectives to be completed to define the escrow as completed
         */
        milestones: {
          /**
           * Text describing the function of the milestone.
           */
          description: string;
    
          /**
           * Milestone status. Ex: Approved, In dispute, etc...
           */
          status?: string;
    
          /**
           * Evidence of work performed by the service provider.
           */
          evidence?: string;
    
          /**
           * Approved flag, only if the escrow is single-release
           */
          approved?: boolean;
        }[];
    
        /**
         * Flags validating certain escrow life states
         */
        flags?: {
          /**
           * Flag indicating that an escrow is in dispute.
           */
          disputed?: boolean;
    
          /**
           * Flag indicating that escrow funds have already been released.
           */
          released?: boolean;
    
          /**
           * Flag indicating that a disputed escrow has already been resolved.
           */
          resolved?: boolean;
    
          /**
           * Flag indicating whether a milestone has been approved by the approver.
           */
          approved?: boolean;
        };
    
        /**
         * Information on the trustline that will manage the movement of funds in escrow
         */
        trustline: {
          /**
           * Public address establishing permission to accept and use a specific token.
           */
          address: string;
        };
    
        /**
         * Whether the escrow is active. This comes from DB, not from the blockchain.
         */
        isActive?: boolean;
      };
    
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    };
    /**
     * Multi Release Update Escrow Payload
     */
    export type UpdateMultiReleaseEscrowPayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Escrow data
       */
      escrow: {
        /**
         * Unique identifier for the escrow
         */
        engagementId: string;
    
        /**
         * Name of the escrow
         */
        title: string;
    
        /**
         * Roles that make up the escrow structure (without receiver, as each milestone has its own receiver)
         */
        roles: {
          /**
           * Address of the entity requiring the service.
           */
          approver: string;
    
          /**
           * Address of the entity providing the service.
           */
          serviceProvider: string;
    
          /**
           * Address of the entity that owns the escrow
           */
          platformAddress: string;
    
          /**
           * Address of the user in charge of releasing the escrow funds to the service provider.
           */
          releaseSigner: string;
    
          /**
           * Address in charge of resolving disputes within the escrow.
           */
          disputeResolver: string;
        };
    
        /**
         * Text describing the function of the escrow
         */
        description: string;
    
        /**
         * Commission that the platform will receive when the escrow is completed
         */
        platformFee: number;
    
        /**
         * Objectives to be completed to define the escrow as completed
         */
        milestones: {
          /**
           * Text describing the function of the milestone.
           */
          description: string;
    
          /**
           * Milestone status. Ex: Approved, In dispute, etc...
           */
          status?: string;
    
          /**
           * Evidence of work performed by the service provider.
           */
          evidence?: string;
    
          /**
           * Amount to be transferred upon completion of this milestone
           */
          amount: number;
    
          /**
           * Address where milestone proceeds will be sent to
           */
          receiver: string;
    
          /**
           * Flags validating certain milestone life states, only if the escrow is multi-release
           */
          flags?: {
            /**
             * Flag indicating that an escrow is in dispute.
             */
            disputed?: boolean;
    
            /**
             * Flag indicating that escrow funds have already been released.
             */
            released?: boolean;
    
            /**
             * Flag indicating that a disputed escrow has already been resolved.
             */
            resolved?: boolean;
    
            /**
             * Flag indicating whether a milestone has been approved by the approver.
             */
            approved?: boolean;
          };
        }[];
    
        /**
         * Information on the trustline that will manage the movement of funds in escrow
         */
        trustline: {
          /**
           * Public address establishing permission to accept and use a specific token.
           */
          address: string;
        };
    
        /**
         * Whether the escrow is active. This comes from DB, not from the blockchain.
         */
        isActive?: boolean;
      };
    
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    };
    import { useUpdateEscrow} from "@trustless-work/escrow/hooks";
    import { UpdateSingleReleaseEscrowPayload, UpdateMultiReleaseEscrowPayload } from "@trustless-work/escrow/types";
    
    /*
     *  useUpdateEscrow
    */
    const { updateEscrow } = useUpdateEscrow();
    
    /* 
     * It returns an unsigned transaction
     * payload should be of type `UpdateSingleReleaseEscrowPayload` or `UpdateMultiReleaseEscrowPayload`
    */
    const { unsignedTransaction } = await updateEscrow(payload);
    
    src/hooks/useUpdateEscrowForm.ts
    import {
      useUpdateEscrow,
      useSendTransaction,
    } from "@trustless-work/escrow/hooks";
    import {
      UpdateSingleReleaseEscrowPayload, UpdateMultiReleaseEscrowPayload
    } from "@trustless-work/escrow/types";
    
    export const useUpdateEscrowForm = () => {
    
     /*
      *  useUpdateEscrow
     */
     const { updateEscrow } = useUpdateEscrow();
     
     /*
      *  useSendTransaction
     */
     const { sendTransaction } = useSendTransaction();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: UpdateSingleReleaseEscrowPayload | UpdateMultiReleaseEscrowPayload) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the updateEscrow function
           * - The result will be an unsigned transaction
           */
          const { unsignedTransaction } = await updateEscrow(
            payload,
            "multi-release"
            // or ...
            // "single-release"
          );
    
          if (!unsignedTransaction) {
            throw new Error(
              "Unsigned transaction is missing from updateEscrow response."
            );
          }
    
          /**
           * @Note:
           * - We need to sign the transaction using your [private key] such as wallet
           * - The result will be a signed transaction
           */
          const signedXdr = await signTransaction({ /* This method should be provided by the wallet */
            unsignedTransaction,
            address: walletAddress || "",
          });
    
          if (!signedXdr) {
            throw new Error("Signed transaction is missing.");
          }
    
          /**
           * @Note:
           * - We need to send the signed transaction to the API
           * - The data will be an SendTransactionResponse
           */
          const data = await sendTransaction(signedXdr);
    
          /**
           * @Responses:
           * data.status === "SUCCESS"
           * - Escrow updated successfully
           * - Show a success toast
           *
           * data.status == "ERROR"
           * - Show an error toast
           */
          if (data.status === "SUCCESS") {
            toast.success("Escrow Updated");
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    import { useChangeMilestoneStatus } from "@trustless-work/escrow/hooks";
    import { ChangeMilestoneStatusPayload } from "@trustless-work/escrow/types";
    
    /*
     *  useChangeMilestoneStatus 
    */
    const { changeMilestoneStatus } = useChangeMilestoneStatus();
    
    /* 
     * It returns an unsigned transaction
     * payload should be of type `ChangeMilestoneStatusPayload`
    */
    const { unsignedTransaction } = await changeMilestoneStatus(payload);
    
    src/hooks/useChangeMilestoneApprovedFlagForm.ts
    import {
      useChangeMilestoneStatus,
      useSendTransaction,
    } from "@trustless-work/escrow/hooks";
    import {
      ChangeMilestoneStatusPayload
    } from "@trustless-work/escrow/types";
    
    export const useChangeMilestoneStatusForm = () => {
    
     /*
      *  useChangeMilestoneApprovedFlag
     */
     const { changeMilestoneApprovedFlag } = useChangeMilestoneStatus();
     
     /*
      *  useSendTransaction
     */
     const { sendTransaction } = useSendTransaction();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: ChangeMilestoneStatusPayload) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the useChangeMilestoneStatus function
           * - The result will be an unsigned transaction
           */
          const { unsignedTransaction } = await useChangeMilestoneStatus(
            payload,
            "multi-release"
            // or ...
            // "single-release"
          );
    
          if (!unsignedTransaction) {
            throw new Error(
              "Unsigned transaction is missing from useChangeMilestoneStatusresponse."
            );
          }
    
          /**
           * @Note:
           * - We need to sign the transaction using your [private key] such as wallet
           * - The result will be a signed transaction
           */
          const signedXdr = await signTransaction({ /* This method should be provided by the wallet */
            unsignedTransaction,
            address: walletAddress || "",
          });
    
          if (!signedXdr) {
            throw new Error("Signed transaction is missing.");
          }
    
          /**
           * @Note:
           * - We need to send the signed transaction to the API
           * - The data will be an SendTransactionResponse
           */
          const data = await sendTransaction(signedXdr);
    
          /**
           * @Responses:
           * data.status === "SUCCESS"
           * - Milestone updated successfully
           * - Show a success toast
           *
           * data.status == "ERROR"
           * - Show an error toast
           */
          if (data.status === "SUCCESS") {
             toast.success(
              `Milestone index - ${payload.milestoneIndex} updated to ${payload.newStatus}`
            );
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    src/hooks/useGetEscrowsFromIndexerByRole.ts
    import {
      useGetEscrowsFromIndexerByRole,
    } from "@trustless-work/escrow/hooks";
    import {
      GetEscrowsFromIndexerByRoleParams, 
    } from "@trustless-work/escrow/types";
    
    export const useGetEscrowsFromIndexerByRoleForm = () => {
    
     /*
      *  useGetEscrowsFromIndexerByRole
     */
     const { getEscrowsByRole } = useGetEscrowsFromIndexerByRole();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: GetEscrowsFromIndexerByRoleParams) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the getEscrowsByRole function
           * - The result will be an escrow
          */
          const escrows = await getEscrowsByRole(payload);
          
          if (!escrows) {
            throw new Error("Escrows not found");
          }
    
          /**
           * @Responses:
           * escrows !== null
           * - Escrows received successfully
           * - Show a success toast
           *
           * escrows === null
           * - Show an error toast
           */
          if (escrows) {
            toast.success("Escrows Received");
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    mainNet
    and
    development
    constants. So you only need to import one of them and pass it to the baseURL prop.
  • apiKey: Authorization provided by our dApp to use the API.

  • Trustless Work React provides the TrustlessWorkConfig to provides all the custom hooks and entities to the whole project. To achieve this you'll need to create a Provider.

    Dependencies

    Configure the provider

    The next step is to configure the Trustless Work provider. You need to configure the following:

    • baseURL: Trustless Work URL, this should be the main or development environment. We provide mainNetand developmentconstants. So you only need to import one of them and pass it to the baseURL prop.

    • apiKey: Authorization provided by our dApp to use the API.

    Trustless Work React provides the TrustlessWorkConfig to provides all the custom hooks and entities to the whole project. To achieve this you'll need to create a Provider.

    3

    Wrap your app in the provider

    Wrap your app in the provider just created.

    Notice that if you are using Next.js app routes, you should place the provider in the root layout file.

    4

    Using hooks and entities

    You can now use the Trustless Work React hooks and entities from any component wrapped by the root provider!

    https://www.npmjs.com/package/@trustless-work/escrowwww.npmjs.com
  • Roles and responsibilities are defined

  • Transaction terms (amount, milestones, fees, trustline) are set

  • The escrow contract is created on-chain

  • Learn More

    2. Funding Phase:

    • Anyone can deposit funds

    • Once funded, the escrow is live and ready for milestone tracking

    . Learn More

    3. Milestone Updates Phase:

    • Marked as completed

    • Optional evidence or proof can be added

    • Provides visibility for review

    Learn More

    4. Approval

    Milestones are reviewed by the Approver.

    • Can approve if conditions are met

    • Can raise a dispute if unsatisfied

    Approval moves the escrow closer to payout.

    Learn More

    5. Release

    The Release Signer authorizes payout.

    • Single-Release → all milestones must be approved before one payout

    • Multi-Release → funds are released milestone by milestone

    Funds are transferred to the Receiver, minus any platform fee.

    Lean More


    ⚠️ Alternative Phase: Dispute Resolution

    If any party raises a dispute, the lifecycle takes a detour:

    Dispute Resolution

    • Dispute Resolver steps in to resolve the conflict

    • Can redirect funds, adjust milestones, or cancel the escrow

    • Outcomes can be:

      • Full refund to client

      • Partial refund

      • No refund (funds go to provider)

    Learn more

    disputeResolver

    string

    Address in charge of resolving disputes within the escrow.

    receiver

    string

    Address where escrow proceeds will be sent to

    Installation Guide

    Schema

    In this section you will be able to see the outline of the types of escrow's that Trustless Work offers. With these diagrams you will be able to know the structure and properties of an escrow both in its Single-Release and Multi-Release versions.

    Single Release Escrow Schema

    Escrow body:

    Key
    Type
    Description

    Roles:

    Name
    Type
    Description

    Milestone:

    Name
    Type
    Description

    Flags:

    Name
    Type
    Description

    Trustline:

    Name
    Type
    Description

    Single Release Escrow Flow:

    Multi Release Escrow Schema

    Key
    Type
    Description

    Roles:

    Name
    Type
    Description

    Milestone:

    Name
    Type
    Description

    Flags:

    Name
    Type
    Description

    Trustline:

    Name
    Type
    Description

    Multi Release Escrow Flow:

    Escrow Properties

    An escrow is just structured data — a JSON body that defines how funds are held, released, and tracked. Each property tells the contract who does what, when funds move, and under which conditions.

    TLDR:

    • Single-Release → all milestones must be approved for one payout.

    • Multi-Release → each milestone unlocks its own payout.

    Below we break down the core properties of every escrow, and then highlight the differences between Single-Release and Multi-Release.

    Single Release escrow

    Core Structure

    • Escrow ID The on-chain identifier of the contract (also the deposit address). This is where funds are actually sent and locked.

    • Engagement ID & Title Configurable strings that help you identify the escrow in your own system — for example, linking it to an invoice, project ID, or marketplace order.

    • Description Human-readable explanation of the escrow’s purpose. Useful for context in dashboards, audits, or dispute resolution.


    Milestones

    Milestones define what must be completed to unlock funds.

    • Single-Release Escrow

      • You can define one or many milestones, but the release is all-or-nothing.

      • Funds are only released once all milestones are approved.

      • Each milestone tracks:

    This structure allows a project to fund and release in phases, not all at once.


    Putting It Together

    • Single-Release = one payout, triggered when all milestones are approved. Amount + release & dispute flags live at the top level of the escrow.

    • Multi-Release = multiple payouts, each milestone has its own amount and flags. The total escrowed amount is distributed across milestones.

    Both share the same core structure — IDs, roles, description, trustline, and platform fee. The difference is:

    • Single-Release → milestones are “checkpoints” for one big release.

    • Multi-Release → milestones are “tranches,” each tied to its own release.


    🚀 Next Steps

    • Choose

    • Assign

    • Follow

    • Test configs in

    Change Milestone Status

    Phase 3 — Change Milestone Status (Signaling Progress)

    Once the escrow is funded, the work begins. This phase is where the Service Provider (or Milestone Marker) communicates progress to everyone else — by signing an update that changes the milestone’s status.

    It’s how the escrow “breathes.” Each update becomes a traceable, on-chain proof of what’s happening off-chain.


    🧱 What “Change Milestone Status” Means

    Every milestone in an escrow has two types of information:

    1. Structural data — defined at deployment (title, receiver, amount).

    2. Dynamic status — updated as work evolves.

    The Change Milestone Status action updates that dynamic state. It’s not limited to pre-set words like pending or done — your platform defines the vocabulary.

    A milestone could move through any flow you design:

    • “Design Started” → “Ready for Review” → “Approved”

    • “Product Packed” → “In Transit” → “Delivered”

    • “Pull Request Opened” → “Code Merged” → “Deployed”

    💬 Trustless Work doesn’t impose statuses. It only ensures that the update comes from the correct role — the Service Provider — and that every change is signed and recorded.


    ✍️ Who Can Perform This Action

    Only the Service Provider (Milestone Marker) can sign milestone status updates. This preserves accountability: progress always originates from the party doing the work.

    Once signed, the update is broadcast on-chain, and the contract records:

    • The new status label (a text string defined by your platform)

    • An optional evidence field

    Other participants — Approver, Release Signer, Platform — can view the update but cannot alter it.


    🧾 Adding Evidence

    Each update can include an evidence input, typically a URL or reference pointing to external proof of progress.

    This could be:

    • A link to a code repository, pull request, or merge commit

    • A delivery receipt, tracking page, or signed document

    • A file stored on decentralized storage like IPFS, Filecoin, or Arweave

    📎 Note: Trustless Work doesn’t store media or documents. It only stores the reference — keeping the escrow lightweight and privacy-respectful. Platforms decide where evidence lives, and how much they want to display publicly.


    🔁 How Platforms Can Use This

    Platforms can build their own workflows on top of this mechanism:

    • Display a real-time progress feed on dashboards

    • Require specific evidence types before allowing “Approve” actions

    • Automate milestone transitions based on external data (e.g., an API confirming delivery)

    Each status update becomes part of the escrow’s event history — a transparent, auditable record of progress.


    ⚙️ What Changes On-Chain

    Every signed update triggers:

    • A Milestone Status Event, visible on Stellar explorers and in the Escrow Viewer

    • A refreshed view of the milestone’s metadata (status, evidence, and timestamp)

    It doesn’t release funds — it just advances the state. The Approval Phase that follows decides whether payment moves forward or the milestone is disputed.


    📦 Outcome of the Change Milestone Status Phase

    By the end of this phase:

    • The Service Provider has submitted a new, verifiable progress update.

    • The escrow now reflects the most recent milestone status and evidence.

    • All participants can see the change on-chain and in the .

    This phase transforms subjective progress into verifiable data — one signed update at a time.

    useApproveMilestone

    Responsible for modifying the "flag" property of a specific milestone in the escrow to approve that milestone.

    Usage

    This custom hook exposes a function along with status flags to manage the approval of a milestone.

    Mutation Function

    approveMilestone

    Returning an unsigned transaction based on the provided payload.

    EscrowType: Specifies the type of escrow. It accepts the following values:

    • multi-release: Allows for multiple releases of funds.

    • single-release: Funds are released in a single transaction.

    ApproveMilestonePayload: An object with fields necessary to approve a milestone. It is applicable for both single-release and multi-release escrow types.

    Parameters:

    Ensure they match: if you choose a "multi-release" type, you must also use a "multi-release" payload.

    • type: Describes the escrow type to be used. Options are "multi-release" or "single-release".

    • payload: Contains the data required for milestone approval.

    Return Value:

    unsignedTransaction: An object representing the constructed transaction, ready to be signed by your wallet and broadcast.


    Usage Example

    Funding Phase

    Phase 2 — Funding (When Logic Meets Capital)

    Once an escrow is deployed, it becomes fundable — meaning any authorized wallet can deposit assets into it. This is where trust becomes tangible: the logic you set in the Initiation Phase now holds real value.

    The Funding Phase signals the start of the agreement in motion. It turns the escrow from an empty container of logic into a live, capital-backed contract.


    💸 Two Ways to Fund

    Every escrow has an Escrow ID (also called Contract ID). Both terms refer to the same on-chain address — where funds are actually held.

    You can fund an escrow in two main ways:

    1. Direct Deposit — Send funds manually to the escrow’s ID using any Stellar wallet.

      • This method is simple and works universally.

      • However, deposits made this way may not automatically trigger indexation events, so if you’re building analytics or dashboards, you’ll need to handle those deposits separately.

    2. Using the “Fund Escrow” Endpoint

    ⚡ In short:

    • Direct deposits work for any wallet or integration.

    • The API endpoint tracks them better for dashboards, automation, and future reconciliations.


    🎯 The Amount and the Balance

    When you deployed your escrow, you defined a target amount — that’s your goal. It’s used to calculate whether the escrow is:

    • Fully Funded – the on-chain balance matches the target amount

    • Partially Funded – the balance is lower than expected

    • Overfunded – extra deposits were made

    What you see on-chain is the balance, which is dynamic:

    • It increases when deposits are made.

    • It decreases when funds are released.

    • It always reflects the escrow’s current real-time state.

    💡 The target amount is static — it represents intent. The balance is live — it represents reality.


    🪙 Compatible Wallets and Assets

    Funds can come from any wallet that supports the trustline you defined during initiation. The most common setup is USDC on Stellar, but any asset with a valid trustline works.

    We recommend using non-custodial wallets for deposits — especially Freighter, our default integration.

    ⚠️ Important note: Some custodial exchanges (like Binance) don’t yet support sending directly to contract addresses. Withdrawals may fail or get flagged as invalid. Always test deposits from a non-custodial wallet first.


    🔄 Advanced Integrations (Optional)

    If you’re building a product where funds flow in from external users or payment processors — like a marketplace or investment pool — you can integrate on-ramp services directly with the escrow.

    We’ve tested integrations where on-ramps send USDC straight to an escrow contract, creating a seamless deposit experience. Each successful deposit triggers an event that platforms can listen to for real-time funding status updates.

    💬 Builders who want to explore advanced integrations can check out our open-source examples and dApp code on GitHub. We’re continuously experimenting with new funding patterns and wallets to improve this experience.


    📦 Outcome of the Funding Phase

    By the end of this phase:

    • Your escrow now holds real assets.

    • Its balance reflects the amount deposited.

    • Events are recorded on-chain for transparency.

    • All participants can independently verify the deposit.

    You can confirm the escrow’s funded state on:

    • 🌐 — for a clear visual of deposits and status

    • 🔍 — for blockchain-level transaction details

    💡 Use the Viewer for clarity, Expert for raw transparency.

    useGetEscrowFromIndexerByContractIds

    Returns the escrows that you're looking for. It comes from our indexer (database) synchronizer with the blockchain.

    Usage

    This custom hook exposes a function to get the escrows that you are looking obtain.

    import { useGetEscrowFromIndexerByContractIds } from "@trustless-work/escrow/hooks";
    import { GetEscrowFromIndexerByContractIdsParams } from "@trustless-work/escrow/types";
    
    /*
     *  useGetEscrowFromIndexerByContractIds
    */
    const { getEscrowByContractIds } = useGetEscrowFromIndexerByContractIds();
    
    /* 
     * It returns the escrow that you are looking for
     * payload should be of type `GetEscrowFromIndexerByContractIdsParams`
    */
    await getEscrowByContractIds(payload);

    Function

    • getEscrowByContractIds Responsible for building and returning data based on the provided payload.

    Argument:

    GetEscrowFromIndexerByContractIdsParams: An object containing the required fields to get the escrows.

    Return Value:

    escrows: The escrows that you are looking for.


    Usage ExampleForm

    EURC Contract Addresses - Circle DocsCircle Docs
    USDC Contract Addresses - Circle DocsCircle Docs

    useReleaseFunds

    You release the escrow funds to the service provider through the approver.

    Usage

    This custom hook exposes a function to release the funds of an escrow.

    Mutation Function

    MCP - SDK

    Configure the Trustless Work SDK MCP on Cursor

    Give your AI agent full power to build, update, and execute escrow actions automatically.

    The Model Context Protocol (MCP) allows Cursor’s AI agent to call external tools and APIs autonomously. By adding the Trustless Work MCP Server, developers can interact with the Trustless Work SDK through Cursor—making escrow creation, milestone updates, approvals, releases, and analysis executable directly from the editor.

    This guide walks you through the exact steps required to connect Cursor to the Trustless Work MCP Server:


    npx trustless-work init
    npx trustless-work add wallet-kit
    page.tsx
    import { WalletButton } from "@/components/tw-blocks/wallet-kit/WalletButtons";
    
    export default function HomePage() {
      return (
        <div className="container mx-auto py-8">
          <div className="flex justify-between items-center mb-8">
            <h1 className="text-3xl font-bold">My Escrows</h1>
            <WalletButton />
          </div>
        </div>
      );
    }
    src/trustless-work-provider.tsx
    "use client"; // make sure this is a client component
    
    import React from "react";
    import {
      // development environment = "https://dev.api.trustlesswork.com"
      development,
    
      // mainnet environment = "https://api.trustlesswork.com"
      mainNet,
      TrustlessWorkConfig,
    } from "@trustless-work/escrow";
    
    interface TrustlessWorkProviderProps {
      children: React.ReactNode;
    }
    
    export function TrustlessWorkProvider({
      children,
    }: TrustlessWorkProviderProps) {
      /**
       * Get the API key from the environment variables
       */
      const apiKey = process.env.NEXT_PUBLIC_API_KEY || "";
    
      return (
        <TrustlessWorkConfig baseURL={development} apiKey={apiKey}>
          {children}
        </TrustlessWorkConfig>
      );
    }
    
    app.tsx
    import { TrustlessWorkProvider} from "@/trustless-work-provider.tsx";
     
    export function App() {
      return (
        <TrustlessWorkProvider>
          <YourApp />
        </TrustlessWorkProvider>
      );
    }
    app/layout.tsx
    import { TrustlessWorkProvider} from "@/trustless-work-provider.tsx";
     
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en">
          <body>
            <TrustlessWorkProvider>
              {children}
            </TrustlessWorkProvider>
          </body>
        </html>
      );
    }
    npm i @trustless-work/escrow
    yarn add @trustless-work/escrow
    npx trustless-work list
    const { selectedEscrow } = useEscrowContext();
    
    const handleSubmit = form.handleSubmit(async (payload) => {             
      /**
       * Create the final payload for the change milestone status mutation
       *
       * @param payload - The payload from the form
       * @returns The final payload for the change milestone status mutation
      */
      const finalPayload: ChangeMilestoneStatusPayload = {
        contractId: selectedEscrow?.contractId || '', // contractId from the selectedEscrow
        milestoneIndex: payload.milestoneIndex,
        newStatus: payload.status,
        newEvidence: payload.evidence || undefined,
        serviceProvider: walletAddress || '',
      };
    
      /**
       * Call the change milestone status mutation
       *
       * @param payload - The final payload for the change milestone statusmutation
       * @param type - The type of the escrow
       * @param address - The address of the escrow
      */
      await changeMilestoneStatus.mutateAsync({
        payload: finalPayload,
        type: selectedEscrow?.type || 'multi-release',
        address: walletAddress || '',
      });
    }
    const { setSelectedEscrow } = useEscrowContext();
    
    const onCardClick = (escrow: Escrow) => {
      setSelectedEscrow(escrow);
      dialogStates.second.setIsOpen(true);
    };
    const { selectedEscrow, updateEscrow } = useEscrowContext();
    
    const handleSubmit = form.handleSubmit(async (payload) => { 
      /**
        * Call the change milestone status mutation
        *
        * @param payload - The final payload for the change milestone status mutation
        * @param type - The type of the escrow
        * @param address - The address of the escrow
      */
      await changeMilestoneStatus.mutateAsync({
        payload: finalPayload,
        type: selectedEscrow?.type || "multi-release", // type from the selectedEscrow
        address: walletAddress || "",
      });
    
      toast.success("Milestone status updated successfully");
    
      // Update the selected escrow in the context with the new status and evidence
      updateEscrow({
        ...selectedEscrow,
        milestones: selectedEscrow?.milestones.map((milestone, index) => {
          if (index === Number(payload.milestoneIndex)) {
            return {
              ...milestone,
              status: payload.status,
              evidence: payload.evidence || undefined,
            };
          }
          return milestone;
        }),
      });
    }
    npx trustless-work escrows // or other parent's blocks directory
    npx trustless-work add escrows
    npx trustless-work add escrows/single-release
    import { useApproveMilestone } from "@trustless-work/escrow/hooks";
    import { ApproveMilestonePayload } from "@trustless-work/escrow/types";
    
    /*
     *  useApproveMilestone
    */
    const { approveMilestone, isPending, isError, isSuccess } = useApproveMilestone();
    
    /* 
     * It returns an unsigned transaction
     * payload should be of type `ApproveMilestonePayload`
    */
    const { unsignedTransaction } = await approveMilestone(payload);
    
    Roles
    Every escrow defines who can act on it:
    • Approver → validates milestone completion

    • Service Provider → delivers the work

    • Platform Address → the platform itself, able to take fees or adjust config before funding

    • Release Signer → executes the release of funds

    • Dispute Resolver → arbitrates conflicts, can re-route funds

    • Receiver → final destination of the funds 👉 See Roles for full detail.

  • Amount & Platform Fee

    • Single-Release: the total amount to be paid once conditions are met, plus an optional platformFee percentage sent to the platform.

    • Multi-Release: the total amount is distributed across milestones (each milestone defines its own amount). The platform fee still applies globally.

  • Trustline Defines the token being used (address and decimals). This is how Stellar escrows know which asset to accept. Typically USDC, but any Stellar-issued token is supported.

  • Flags Internal state markers that describe what’s happening:

    • disputed → a party raised a dispute

    • released → funds have already been released

    • resolved → a dispute has been settled

    • approved (Multi-Release only) → milestone has been approved by approver

    • description → what’s being delivered

    • status → pending, approved, in dispute, etc.

    • evidence (optional) → proof of delivery

    • approvedFlag → true when the approver signs off

  • Multi-Release Escrow

    • Each milestone has the same properties as the single release, plus its own amount and flags.

    • When a milestone is approved, its funds can be released without waiting for others.

    • Milestones include:

      • amount → how much is unlocked upon approval

      • flags → released, disputed, resolved

      • Receiver → final destination of the funds

  • Escrow Type
    Roles
    Lifecycle Phases
    deploy in dApp
    Escrow Viewer
    — via the Trustless Work API or dApp.
    • This option generates and signs the transaction from your connected wallet (e.g., Freighter).

    • It also emits a Deposit Event on-chain, making it easier for our indexer (and your platform) to track and verify deposits automatically.

    • This is the method we recommend — it’s what powers the “Fund” buttons in our Backoffice and Demo dApps.

    Escrow Viewer
    Stellar Expert

    number

    Amount to be transferred upon completion of escrow milestones

    plataformFee

    number

    Commission that the platform will receive when the escrow is completed

    milestones

    Milestone<Array>

    Objectives to be completed to define the escrow as completed

    flags

    Flags Object

    Flags validating certain escrow life states

    trustline

    Trustline Object

    Information on the trustline that will manage the movement of funds in escrow

    disputeResolver

    string

    Address in charge of resolving disputes within the escrow.

    receiver

    string

    Address where escrow proceeds will be sent to

    plataformFee

    number

    Commission that the platform will receive when the escrow is completed

    milestones

    Milestone<Array>

    Objectives to be completed to define the escrow as completed

    trustline

    Trustline Object

    Information on the trustline that will manage the movement of funds in escrow

    disputeResolver

    string

    Address in charge of resolving disputes within the escrow.

    amount

    number

    Amount to be transferred upon completion of escrow milestones.

    receiver

    string

    Address where escrow proceeds will be sent to

    engagementId

    string

    Unique identifier for the escrow

    title

    string

    Name of the escrow

    roles

    Roles Object

    Roles that make up the escrow structure

    description

    string

    Text describing the function of the escrow

    approver

    string

    Address of the entity requiring the service.

    serviceProvider

    string

    Address of the entity providing the service.

    plataformAddress

    string

    Address of the entity that owns the escrow

    releaseSigner

    string

    Address of the user in charge of releasing the escrow funds to the service provider.

    description

    string

    Text describing the function of the milestone.

    status

    string

    Milestone status. Ex: Approved, In dispute, etc...

    evidence

    string (optional)

    Evidence of work performed by the service provider.

    approved

    boolean

    Flag indicating whether a milestone has been approved by the approver.

    disputed

    boolean

    Flag indicating that an escrow is in dispute.

    released

    boolean

    Flag indicating that escrow funds have already been released.

    resolved

    boolean

    Flag indicating that a disputed escrow has already been resolved.

    address

    string

    Public address establishing permission to accept and use a specific token.

    engagementId

    string

    Unique identifier for the escrow

    title

    string

    Name of the escrow

    description

    string

    Text describing the function of the escrow

    roles

    Roles Object

    Roles that make up the escrow structure

    approver

    string

    Address of the entity requiring the service.

    serviceProvider

    string

    Address of the entity providing the service.

    plataformAddress

    string

    Address of the entity that owns the escrow

    releaseSigner

    string

    Address of the user in charge of releasing the escrow funds to the service provider.

    description

    string

    Text describing the function of the milestone.

    status

    string

    Milestone status. Ex: Approved, In dispute, etc...

    flags

    Flags Object

    Flags validating certain escrow life states.

    evidence

    string (optional)

    Evidence of work performed by the service provider.

    disputed

    boolean

    Flag indicating that an escrow is in dispute.

    released

    boolean

    Flag indicating that escrow funds have already been released.

    resolved

    boolean

    Flag indicating that a disputed escrow has already been resolved.

    approved

    boolean

    Flag indicating whether a milestone has been approved by the approver.

    address

    string

    Public address establishing permission to accept and use a specific token.

    decimals

    number

    Number of decimals into which the token is divided.

    symbol

    string

    Official abbreviation representing the token in wallets, exchanges, and documentation.

    amount

    https://www.npmjs.com/package/@trustless-work/blockswww.npmjs.com
    Approve Milestone
    Get Escrows by Contract ID
    releaseFunds

    Responsible for building and returning an unsigned transaction based on the provided payload.

    EscrowType: Specifies the type of escrow. It accepts the following values:

    • multi-release: Allows for multiple releases of funds.

    • single-release: Funds are released in a single transaction.

    SingleReleaseReleaseFundsPayload: An object with fields necessary to release a single-release escrow.

    MultiReleaseReleaseFundsPayload: An object with fields necessary to release a multi-release escrow by a specific milestone.

    Parameters:

    Ensure they match: if you choose a "multi-release" type, you must also use a "multi-release" payload.

    • type: Describes the escrow type to be used. Options are "multi-release" or "single-release".

    • payload: An object containing the required fields to release an escrow or milestone.

    Return Value:

    unsignedTransaction: An object representing the constructed transaction, ready to be signed by your wallet and broadcast.


    Usage Example

    Release Funds
    src/trustless-work-provider.tsx
    "use client"; // make sure this is a client component
    
    import React from "react";
    import {
      // development environment = "https://dev.api.trustlesswork.com"
      development,
    
      // mainnet environment = "https://api.trustlesswork.com"
      mainNet,
      TrustlessWorkConfig,
    } from "@trustless-work/escrow";
    
    interface TrustlessWorkProviderProps {
      children: React.ReactNode;
    }
    
    export function TrustlessWorkProvider({
      children,
    }: TrustlessWorkProviderProps) {
      /**
       * Get the API key from the environment variables
       */
      const apiKey = process.env.NEXT_PUBLIC_API_KEY || "";
    
      return (
        <TrustlessWorkConfig baseURL={development} apiKey={apiKey}>
          {children}
        </TrustlessWorkConfig>
      );
    }
    
    src/hooks/useApproveMilestoneForm.ts
    import {
      useApproveMilestone,
      useSendTransaction,
    } from "@trustless-work/escrow/hooks";
    import {
      ApproveMilestonePayload
    } from "@trustless-work/escrow/types";
    
    export const useApproveMilestoneForm = () => {
    
     /*
      *  useApproveMilestone
     */
     const { approveMilestone } = useApproveMilestone();
     
     /*
      *  useSendTransaction
     */
     const { sendTransaction } = useSendTransaction();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: ApproveMilestonePayload) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the approveMilestone function
           * - The result will be an unsigned transaction
           */
          const { unsignedTransaction } = await approveMilestone(
            payload,
            "multi-release"
            // or ...
            // "single-release"
          );
    
          if (!unsignedTransaction) {
            throw new Error(
              "Unsigned transaction is missing from approveMilestone response."
            );
          }
    
          /**
           * @Note:
           * - We need to sign the transaction using your [private key] such as wallet
           * - The result will be a signed transaction
           */
          const signedXdr = await signTransaction({ /* This method should be provided by the wallet */
            unsignedTransaction,
            address: walletAddress || "",
          });
    
          if (!signedXdr) {
            throw new Error("Signed transaction is missing.");
          }
    
          /**
           * @Note:
           * - We need to send the signed transaction to the API
           * - The data will be an SendTransactionResponse
           */
          const data = await sendTransaction(signedXdr);
    
          /**
           * @Responses:
           * data.status === "SUCCESS"
           * - Milestones approved successfully
           * - Show a success toast
           *
           * data.status == "ERROR"
           * - Show an error toast
           */
          if (data.status === "SUCCESS") {
            toast.success(
              `Milestone index - ${payload.milestoneIndex} has been approved`
            );
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    src/hooks/useGetEscrowFromIndexerByContractIds.ts
    import {
      useGetEscrowFromIndexerByContractIds,
    } from "@trustless-work/escrow/hooks";
    import {
      GetEscrowFromIndexerByContractIdsParams, 
    } from "@trustless-work/escrow/types";
    
    export const useGetEscrowFromIndexerByContractIdsForm = () => {
    
     /*
      *  useGetEscrowFromIndexerByContractIds
     */
     const { getEscrowByContractIds } = useGetEscrowFromIndexerByContractIds();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: GetEscrowFromIndexerByContractIdsParams) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the getEscrowByContractIds function
           * - The result will be escrows
          */
          const escrows = await getEscrowByContractIds(payload);
          
          if (!escrows) {
            throw new Error("Escrows not found");
          }
    
          /**
           * @Responses:
           * escrows !== null
           * - Escrows received successfully
           * - Show a success toast
           *
           * escrows === null
           * - Show an error toast
           */
          if (escrows) {
            toast.success("Escrows Received");
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    import { useReleaseFunds } from "@trustless-work/escrow/hooks";
    import { SingleReleaseReleaseFundsPayload, MultiReleaseReleaseFundsPayload } from "@trustless-work/escrow/types";
    
    /*
     *  useReleaseFunds
    */
    const { releaseFunds } = useReleaseFunds();
    
    /* 
     * It returns an unsigned transaction
     * payload should be of type `MultiReleaseReleaseFundsPayload` or `SingleReleaseReleaseFundsPayload`
    */
    const { unsignedTransaction } = await releaseFunds(payload);
    
    src/hooks/useReleaseFundsForm.ts
    import {
      useReleaseFunds,
      useSendTransaction,
    } from "@trustless-work/escrow/hooks";
    import {
      SingleReleaseReleaseFundsPayload, MultiReleaseReleaseFundsPayload
    } from "@trustless-work/escrow/types";
    
    export const useReleaseFundsForm = () => {
    
     /*
      *  useReleaseFunds
     */
     const { releaseFunds } = useReleaseFunds();
     
     /*
      *  useSendTransaction
     */
     const { sendTransaction } = useSendTransaction();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: MultiReleaseReleaseFundsPayload | SingleReleaseReleaseFundsPayload) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the releaseFunds function
           * - The result will be an unsigned transaction
           */
          const { unsignedTransaction } = await releaseFunds(
            payload,
            "multi-release"
            // or ...
            // "single-release"
          );
    
          if (!unsignedTransaction) {
            throw new Error(
              "Unsigned transaction is missing from useReleaseFunds."
            );
          }
    
          /**
           * @Note:
           * - We need to sign the transaction using your [private key] such as wallet
           * - The result will be a signed transaction
           */
          const signedXdr = await signTransaction({ /* This method should be provided by the wallet */
            unsignedTransaction,
            address: walletAddress || "",
          });
    
          if (!signedXdr) {
            throw new Error("Signed transaction is missing.");
          }
    
          /**
           * @Note:
           * - We need to send the signed transaction to the API
           * - The data will be an SendTransactionResponse
           */
          const data = await sendTransaction(signedXdr);
    
          /**
           * @Responses:
           * data.status === "SUCCESS"
           * - Escrow released successfully
           * - Show a success toast
           *
           * data.status == "ERROR"
           * - Show an error toast
           */
          if (data.status === "SUCCESS") {
             toast.success("The escrow has been released");
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    1. Open Cursor Settings

    You can access MCP settings in two ways:

    Method A: From the UI

    1. Open Cursor

    2. Click the ⚙️ Settings icon (top-right corner)

    Method B: From the menu

    • File → Preferences → Cursor Settings


    2. Navigate to the MCP Configuration Section

    In the left-side settings sidebar, look for:

    MCP → Tools/MCP

    This section lists all local and hosted MCP servers currently available to Cursor.


    3. Add the Trustless Work MCP Server

    Click:

    ➕ Add New MCP Server

    Cursor will either:

    • Create an mcp.json file in your project, or

    • Open your existing one.

    Paste the following configuration:

    Save the file (Cmd+S / Ctrl+S)


    4. Verify the MCP Server is Running

    Go back to:

    Settings → MCP → MCP Servers

    Cursor will automatically:

    1. Detect the new entry (this may take a while)

    2. Attempt to connect

    3. Install and start the MCP server

    A successful connection shows:

    • Green "Connected" indicator

    • A list of tools / methods exposed by the Trustless Work MCP

    If the server does not start:

    • Click ↻ Reload Servers

    • Restart Cursor

    • Ensure you copied the URL exactly


    5. Start Using Trustless Work Tools in Cursor

    Once the MCP is connected:

    • Open a new chat inside Cursor

    • Switch to Agent Mode

    • Start asking Cursor to perform Trustless Work operations (You may want to have trustlesswork-sdk and stellar-wallet-kit installed)

    Example prompts:

    • “Create a new multi-release escrow with the SDK.”

    • “Generate code to call the changeMilestoneStatus endpoint.”

    • “Show me how to sign a transaction for releaseFunds.”

    • “Use the MCP tool to call /escrow/multi-release/change-milestone-status.”

    Cursor will now:

    • Query the MCP server

    • Fetch schemas and tools

    • Generate correct SDK code

    • Execute actions directly through the MCP

    This effectively becomes your fully autonomous Trustless Work coding agent.


    6. Troubleshooting

    Server doesn’t appear in the list

    Check that mcp.json is placed in your project root.

    Connection error

    Verify:

    • URL has no trailing slash

    • "type": "streamable-http"

    • Your firewall/ISP doesn’t block outgoing requests

    Cursor doesn’t use the MCP tools in chat

    Ensure:

    • You have Agent Mode activated

    • The correct workspace is selected

    • No syntax errors in the JSON file


    What This Unlocks

    ✔ Autonomous escrow interactions ✔ Automatic code generation with TW SDK ✔ XDR signing workflows ✔ Transaction submission flows ✔ API wrapper generation ✔ Integration recipes ✔ Full Trustless Work lifecycle automation inside Cursor

    You’ve effectively enabled AI-native escrow development. Agents can now build, update, and maintain escrow logic—end to end.


    Logo

    Escrow Types

    Trustless Work supports multiple escrow types, each tailored for different workflows. Whether you're building a marketplace, a grant platform, or a gig app, choosing the right escrow logic helps you balance simplicity, flexibility, and trust.


    Single-Release Escrow

    A Single-Release Escrow holds funds until all milestones (verifiable checkpoints, like "design done" or "code deployed") are completed and approved. Only then is the entire amount released in one go. It’s built for projects where trust builds across multiple steps but payout happens once.

    Build it like this:

    1. Deposit: Funds are locked upfront by any party (e.g., a client) via a Stellar wallet.

    2. Milestone Completion: The Service Provider (e.g., a freelancer) marks each milestone complete (e.g., "Logo delivered," "Site live").

    3. Approval & Release: The Approver (e.g., the client) verifies all milestones. Once all are signed off, the Release Signer (e.g., the platform) releases the full amount to the Receiver, minus any Platform fee.

    Example: A freelancer on a marketplace delivers a website (milestones: wireframe, design, launch). The buyer (Approver) confirms all are done, and the platform (Release Signer) releases the full payment.


    Multi-Release Escrow

    A Multi-Release Escrow releases funds incrementally as each milestone is completed and approved. It’s designed for staged projects where trust and payments build step-by-step, reducing risk.

    Build it like this:

    1. Deposit: Funds are deposited upfront or in parts via Stellar wallets.

    2. Milestone Completion & Review: The Service Provider (e.g., a DAO contributor) marks each milestone complete (e.g., "Prototype built"). The Approver (e.g., DAO voters) reviews each.

    3. Incremental Release: For each approved milestone, the Release Signer (e.g., the platform) releases that milestone’s portion of funds to the Receiver. Dispute Resolvers handle conflicts, adjusting amounts or canceling if needed. (Note: Per-milestone payouts are coming, per doc.)

    Example: A DAO funds a developer for a project (milestones: code v1, v2, v3). Each milestone’s approval releases a portion of stablecoins, with a Dispute Resolver stepping in if voters contest progress.

    Why use it?

    • ✅ Flexible: Pay per milestone, not all at once.


    Quick Tips:

    • Use Single-Release to get started fast.

    • Use Multi-Release when you need milestone-based control.

    • All escrows are non-custodial, programmable, and stablecoin-native.


    JSON Examples

    Here are two minimal JSON snippets that highlight the structural difference between Single-Release and Multi-Release escrows. These aren’t full schemas, just the essentials so a builder can “see it” at a glance.


    📝 Example: Single-Release (with multiple milestones)

    👉 Even with multiple milestones, all must be approved before the single payout of 1000 USDC is released.


    📝 Example: Multi-Release

    👉 Here, each milestone has its own amount and flags. Funds are released milestone-by-milestone (500 + 500).

    useResolveDispute

    Resolves escrow disputes by distributing funds to the approver and service provider as determined by the dispute resolver.

    Usage

    This custom hook exposes a function to resolve a dispute in an escrow.

    Mutation Function

    resolveDispute

    Responsible for building and returning an unsigned transaction based on the provided payload.

    EscrowType: Specifies the type of escrow. It accepts the following values:

    • multi-release: Allows for multiple releases of funds.

    • single-release: Funds are released in a single transaction.

    SingleReleaseResolveDisputePayload: An object with fields necessary to resolve a single-release escrow.

    MultiReleaseResolveDisputePayload: An object with fields necessary to resolve a multi-release escrow by milestone.

    Parameters:

    Ensure they match: if you choose a "multi-release" type, you must also use a "multi-release" payload.

    • type: Describes the escrow type to be used. Options are "multi-release" or "single-release".

    • payload: An object containing the required fields to resolve a dispute.

    Return Value:

    unsignedTransaction: An object representing the constructed transaction, ready to be signed by your wallet and broadcast.


    Usage Example

    https://www.npmjs.com/package/@trustless-work/blockswww.npmjs.com
    "trustlesswork": {
      "type": "streamable-http",
      "url": "https://mcp.trustlesswork.com/mcp",
      "headers": {}
    }
    {
      "trustlesswork": {
        "type": "streamable-http",
        "url": "https://mcp.trustlesswork.com/mcp",
        "headers": {}
      }
    }
    import { useResolveDispute } from "@trustless-work/escrow/hooks";
    import { MultiReleaseResolveDisputePayload, SingleReleaseResolveDisputePayload } from "@trustless-work/escrow/types";
    
    /*
     *  useResolveDispute
    */
    const { resolveDispute } = useResolveDispute();
    
    /* 
     * It returns an unsigned transaction
     * payload should be of type `MultiReleaseResolveDisputePayload` or `SingleReleaseResolveDisputePayload`
    */
    const { unsignedTransaction } = await resolveDispute(payload);
    

    Aspect

    Single-Release

    Multi-Release

    Payouts

    All at once, post all milestones

    Per milestone

    Use Case

    Freelance with staged checks

    Grant disbursements

    Complexity

    Medium (multiple milestones, one release)

    High (staged releases)

    single-release
    Resolve Dispute
    {
      "contractId": "C...ESCROWADDRESS",
      "engagementId": "order-123",
      "title": "Website Development",
      "description": "Build and deliver a marketing website",
      "roles": {
        "approver": "G...CLIENT",
        "serviceProvider": "G...FREELANCER",
        "releaseSigner": "G...SIGNER",
        "platformAddress": "G...PLATFORM",
        "disputeResolver": "G...RESOLVER",
        "receiver": "G...FREELANCER"
      },
      "amount": 1000,
      "platformFee": 0.5,
      "milestones": [
        {
          "description": "Deliver homepage design",
          "status": "Approved",
          "approved": true
        },
        {
          "description": "Deploy full website",
          "status": "Pending",
          "approved": false
        }
      ],
      "flags": {
        "disputed": false,
        "released": false,
        "resolved": false
      },
      "trustline": {
        "address": "G...USDCISSUER",
      }
    }
    {
      "contractId": "C...ESCROWADDRESS",
      "engagementId": "grant-456",
      "title": "Research Grant",
      "description": "Funding project in two phases",
      "roles": {
        "approver": "G...FUNDER",
        "serviceProvider": "G...RESEARCHER",
        "releaseSigner": "G...SIGNER",
        "platformAddress": "G...PLATFORM",
        "disputeResolver": "G...RESOLVER",
      },
      "platformFee": 0.5,
      "milestones": [
        {
          "description": "Submit interim report",
          "amount": 500,
          "status": "Approved",
          "flags": { "approved": true, "released": true, "disputed": false, "resolved": false },
          "receiver": "G...RESEARCHER"
        },
        {
          "description": "Publish final report",
          "amount": 500,
          "status": "Pending",
          "flags": { "approved": false, "released": false, "disputed": false, "resolved": false },
          "receiver": "G...RESEARCHER"
        }
      ],
      "trustline": {
        "address": "G...USDCISSUER",
      }
    }
    src/hooks/useResolveDisputeForm.ts
    import {
      useResolveDispute,
      useSendTransaction,
    } from "@trustless-work/escrow/hooks";
    import {
      MultiReleaseResolveDisputePayload, SingleReleaseResolveDisputePayload
    } from "@trustless-work/escrow/types";
    
    export const useStartDisputeForm = () => {
    
     /*
      *  useResolveDispute
     */
     const { resolveDispute } = useResolveDispute();
     
     /*
      *  useSendTransaction
     */
     const { sendTransaction } = useSendTransaction();
    
    /*
     * onSubmit function, this could be called by form button
    */
     const onSubmit = async (payload: MultiReleaseResolveDisputePayload | SingleReleaseResolveDisputePayload) => {
    
        try {
          /**
           * API call by using the trustless work hooks
           * @Note:
           * - We need to pass the payload to the resolveDispute function
           * - The result will be an unsigned transaction
           */
          const { unsignedTransaction } = await resolveDispute(
            payload,
            "multi-release"
            // or ...
            //"single-release"
          );
    
          if (!unsignedTransaction) {
            throw new Error(
              "Unsigned transaction is missing from resolveDispute."
            );
          }
    
          /**
           * @Note:
           * - We need to sign the transaction using your [private key] such as wallet
           * - The result will be a signed transaction
           */
          const signedXdr = await signTransaction({ /* This method should be provided by the wallet */
            unsignedTransaction,
            address: walletAddress || "",
          });
    
          if (!signedXdr) {
            throw new Error("Signed transaction is missing.");
          }
    
          /**
           * @Note:
           * - We need to send the signed transaction to the API
           * - The data will be an SendTransactionResponse
           */
          const data = await sendTransaction(signedXdr);
    
          /**
           * @Responses:
           * data.status === "SUCCESS"
           * - Dispute resolved successfully
           * - Show a success toast
           *
           * data.status == "ERROR"
           * - Show an error toast
           */
          if (data.status === "SUCCESS") {
             toast.success("Dispute Resolved");
          }
        } catch (error: unknown) {
          // catch error logic
        }
      };
    }
    
    Logo
    Logo
    Trustless Work | Blocksblocks.trustlesswork.com
    https://github.com/Trustless-Work/react-library-trustless-work-blocksgithub.com
    Logo
    Logo
    Logo
    Logo

    Payloads

    Escrow's Payload Entity

    import { EscrowType, SingleReleaseEscrowStatus } from "./types";
    import { MultiReleaseEscrow, Role, SingleReleaseEscrow } from "./types.entity";
    
    /**
     * Documentation: https://docs.trustlesswork.com/trustless-work/developer-resources/quickstart/integration-demo-project/entities
     */
    
    // ----------------- Milestone Payloads -----------------
    /**
     * Single Release Milestone Payload
     */
    export type SingleReleaseMilestonePayload = {
      /**
       * Text describing the function of the milestone
       */
      description: string;
    };
    
    /**
     * Multi Release Milestone Payload
     */
    export type MultiReleaseMilestonePayload = {
      /**
       * Text describing the function of the milestone
       */
      description: string;
      /**
       * Amount to be transferred upon completion of this milestone
       */
      amount: number;
      /**
       * Address where milestone proceeds will be sent to
       */
      receiver: string;
    };
    
    // ----------------- Initialize Escrow -----------------
    /**
     * Single Release Initialize Escrow Payload
     */
    export type InitializeSingleReleaseEscrowPayload = Omit<
      SingleReleaseEscrow,
      "contractId" | "balance" | "milestones"
    > & {
      /**
       * Objectives to be completed to define the escrow as completed
       */
      milestones: SingleReleaseMilestonePayload[];
    };
    
    /**
     * Multi Release Initialize Escrow Payload
     */
    export type InitializeMultiReleaseEscrowPayload = Omit<
      MultiReleaseEscrow,
      "contractId" | "balance" | "milestones"
    > & {
      /**
       * Objectives to be completed to define the escrow as completed
       */
      milestones: MultiReleaseMilestonePayload[];
    };
    
    // ----------------- Update Escrow -----------------
    /**
     * Single Release Update Escrow Payload
     */
    export type UpdateSingleReleaseEscrowPayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Escrow data
       */
      escrow: Omit<SingleReleaseEscrow, "contractId" | "signer" | "balance"> & {
        /**
         * Whether the escrow is active. This comes from DB, not from the blockchain.
         */
        isActive?: boolean;
      };
    
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    };
    
    /**
     * Multi Release Update Escrow Payload
     */
    export type UpdateMultiReleaseEscrowPayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Escrow data
       */
      escrow: Omit<MultiReleaseEscrow, "contractId" | "signer" | "balance"> & {
        /**
         * Whether the escrow is active. This comes from DB, not from the blockchain.
         */
        isActive?: boolean;
      };
    
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    };
    
    // ----------------- Change Milestone Status -----------------
    /**
     * Change Milestone Status Payload, this can be a single-release or multi-release
     */
    export type ChangeMilestoneStatusPayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Index of the milestone to be updated
       */
      milestoneIndex: string;
    
      /**
       * New status of the milestone
       */
      newStatus: string;
    
      /**
       * New evidence of work performed by the service provider.
       */
      newEvidence?: string;
    
      /**
       * Address of the entity providing the service.
       */
      serviceProvider: string;
    };
    
    // ----------------- Approve Milestone -----------------
    /**
     * Approve Milestone Payload, this can be a single-release or multi-release
     */
    export type ApproveMilestonePayload = Omit<
      ChangeMilestoneStatusPayload,
      "serviceProvider" | "newStatus"
    > & {
      /**
       * Address of the entity requiring the service.
       */
      approver: string;
    };
    
    // ----------------- Start Dispute -----------------
    /**
     * Single Release Start Dispute Payload. This starts a dispute for the entire escrow.
     */
    export type SingleReleaseStartDisputePayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    };
    
    /**
     * Multi Release Start Dispute Payload. This starts a dispute for a specific milestone.
     */
    export type MultiReleaseStartDisputePayload =
      SingleReleaseStartDisputePayload & {
        /**
         * Index of the milestone to be disputed
         */
        milestoneIndex: string;
      };
    
    // ----------------- Resolve Dispute -----------------
    /**
     * Resolve Dispute Payload
     */
    export type SingleReleaseResolveDisputePayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address in charge of resolving disputes within the escrow.
       */
      disputeResolver: string;
    
      /**
       * Distributions of the escrow amount to the receivers.
       */
      distributions: [
        {
          /**
           * Address of the receiver
           */
          address: string;
          /**
           * Amount to be transferred to the receiver. All the amount must be equal to the total amount of the escrow.
           */
          amount: number;
        },
      ];
    };
    
    /**
     * Multi Release Resolve Dispute Payload
     */
    export type MultiReleaseResolveDisputePayload =
      SingleReleaseResolveDisputePayload & {
        /**
         * Index of the milestone to be resolved
         */
        milestoneIndex: string;
      };
    
    // ----------------- Withdraw Remaining Funds -----------------
    /**
     * Withdraw remaining funds
     */
    export type WithdrawRemainingFundsPayload = SingleReleaseResolveDisputePayload;
    
    // ----------------- Fund Escrow -----------------
    /**
     * Fund Escrow Payload, this can be a single-release or multi-release
     */
    export type FundEscrowPayload = {
      /**
       * Amount to be transferred upon completion of escrow milestones
       */
      amount: number;
    
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address of the user signing the contract transaction
       */
      signer: string;
    };
    
    // ----------------- Get Escrows From Indexer -----------------
    /**
     * Get Escrows From Indexer Params
     */
    export type GetEscrowsFromIndexerParams = {
      /**
       * Page number. Pagination
       */
      page?: number;
    
      /**
       * Sorting direction. Sorting
       */
      orderDirection?: "asc" | "desc";
    
      /**
       * Order by property. Sorting
       */
      orderBy?: "createdAt" | "updatedAt" | "amount";
    
      /**
       * Created at = start date. Filtering
       */
      startDate?: string;
    
      /**
       * Created at = end date. Filtering
       */
      endDate?: string;
    
      /**
       * Max amount. Filtering
       */
      maxAmount?: number;
    
      /**
       * Min amount. Filtering
       */
      minAmount?: number;
    
      /**
       * Is active. Filtering
       */
      isActive?: boolean;
    
      /**
       * Escrow that you are looking for. Filtering
       */
      title?: string;
    
      /**
       * Engagement ID. Filtering
       */
      engagementId?: string;
    
      /**
       * Status of the single-release escrow. Filtering
       */
      status?: SingleReleaseEscrowStatus;
    
      /**
       * Type of the escrow. Filtering
       */
      type?: EscrowType;
    
      /**
       * If true, the escrows will be validated on the blockchain to ensure data consistency.
       * This performs an additional verification step to confirm that the escrow data
       * returned from the indexer matches the current state on the blockchain.
       * Use this when you need to ensure the most up-to-date and accurate escrow information.
       * If you active this param, your request will take longer to complete.
       */
      validateOnChain?: boolean;
    };
    
    export type GetEscrowsFromIndexerBySignerParams =
      GetEscrowsFromIndexerParams & {
        /**
         * Address of the user signing the contract transaction.
         */
        signer: string;
      };
    
    export type GetEscrowsFromIndexerByRoleParams = GetEscrowsFromIndexerParams & {
      /**
       * Role of the user. Required
       */
      role: Role;
    
      /**
       * Address of the owner of the escrows. If you want to get all escrows from a specific role, you can use this parameter. But with this parameter, you can't use the signer parameter.
       */
      roleAddress: string;
    };
    
    export type GetEscrowFromIndexerByContractIdsParams = {
      /**
       * IDs (addresses) that identifies the escrow contracts.
       */
      contractIds: string[];
    
      /**
       * If true, the escrows will be validated on the blockchain to ensure data consistency.
       * This performs an additional verification step to confirm that the escrow data
       * returned from the indexer matches the current state on the blockchain.
       * Use this when you need to ensure the most up-to-date and accurate escrow information.
       * If you active this param, your request will take longer to complete.
       */
      validateOnChain?: boolean;
    };
    
    // ----------------- Release Funds -----------------
    /**
     * Single Release Release Funds Payload
     */
    export type SingleReleaseReleaseFundsPayload = {
      /**
       * ID (address) that identifies the escrow contract
       */
      contractId: string;
    
      /**
       * Address of the user in charge of releasing the escrow funds to the service provider.
       */
      releaseSigner: string;
    };
    
    /**
     * Multi Release Release Funds Payload
     */
    export type MultiReleaseReleaseFundsPayload =
      SingleReleaseReleaseFundsPayload & {
        /**
         * Index of the milestone to be released
         */
        milestoneIndex: string;
      };
    
    // ----------------- Get Balance -----------------
    /**
     * Get Balance Params
     */
    export type GetBalanceParams = {
      /**
       * Addresses of the escrows to get the balance
       */
      addresses: string[];
    };
    
    // ----------------- Update From Transaction Hash -----------------
    /**
     * Payload for updating escrow data from a transaction hash.
     */
    export type UpdateFromTxHashPayload = {
      /**
       * Transaction hash to be used for the update.
       */
      txHash: string;
    };

    Vibe Coding - Blocks


    ⚡ VibeCoding Guide — Escrow Blocks (Single-File AI Context)

    This page is engineered for bytecoders/agent workflows. You can export to PDF or Markdown and feed it to your copilot, Cursor, or fine-tuning pipelines. Our docs are explicitly optimized to be exported as AI context and can be queried via the docs search bar in natural language.


    0) What this file is

    • Purpose: Give an AI (or a very fast human) all the context needed to install, wire, and use Trustless Work Escrow Blocks with Next.js.

    • Scope: Installation, required providers (order matters), commands to add blocks, example pages, dependency rules, actions, and troubleshooting.

    • Audience: Builders, PMs, and AIs doing codegen for single-release and multi-release escrow UIs.


    1) Quick mental model

    • Escrow Blocks = prebuilt React components + hooks that talk to the Trustless Work API/SDK for escrow lifecycles.

    • Providers must wrap your app in a specific order (React Query → TW → Wallet → Escrow → Dialogs → Amount). Do not reorder.

    • Listings (by role / by signer) show escrows and open detail dialogs with context-aware actions. Some actions ship commented; enable the version matching your escrow type (single or multi).


    2) Project bootstrap (Next.js + Blocks)

    2.1 Create the app

    2.2 Install Escrow Blocks

    2.3 Run the CLI (recommended)

    What it does (summary):

    • Installs deps (@tanstack/react-query, forms/validation libs, shadcn/ui).

    • Generates .twblocks.json.

    • Offers to wire providers in app/layout.tsx for you.

    You can also add modules incrementally with npx trustless-work add <module>.


    3) Environment

    Create .env.local (reads can work without a key; write flows need it):

    Get your API key in the dApp when ready to move beyond read-only dev flows. (Docs index + dApp flow referenced elsewhere.)


    4) Provider stack (order is critical)

    If you skipped CLI wiring, add these providers in this exact order.

    Why it matters: The blocks depend on React Query + Trustless Work context + wallet + escrow state + dialogs/amount contexts. Reordering breaks hooks and UI flows.


    5) Add modules & blocks (CLI)

    Run these once per project:


    6) Your first page (wallet + init + listings)

    What you’ll see: wallet connect, “Initialize Escrow” (single-release), and a cards grid filtered by role (actions are role/state-aware).


    7) Actions area: enabling the right buttons

    Inside listings’ detail dialog, some actions are commented so you can enable only what you need per escrow type:

    When ready, import & enable:

    Rule of thumb: Use single-release actions for single escrows; use multi- components for multi escrows. Listings are shared, funding/approve/status are shared via single-multi-release.


    8) Dependency rules (practical)

    Listings (by role / by signer) need:

    • wallet-kit, providers, tanstack, helpers, handle-errors, plus lifecycle blocks for the actions you’ll expose.

    Single-release or Multi-release actions need:

    • wallet-kit, providers, tanstack, helpers (+ handle-errors), and the corresponding block set(s).

    Provider order must match section 4.


    9) Usage flow (testnet demo path)

    1. Connect wallet (Freighter).

    2. Initialize escrow (single or multi).

    3. Fund the escrow (shared dialogs/buttons).

    4. Change milestone status, approve, and then release (release-all for single; release milestone

    Tip: Listings filter by role; the visible actions depend on your role + escrow state. Add the USDC trustline in Freighter to interact smoothly.


    10) Troubleshooting (fast fixes)

    • Buttons do nothing / disabled: Your account doesn’t have the required role, the escrow state doesn’t allow that action, or the escrow isn’t funded/approved yet.

    • Hooks failing / context error: Provider order is wrong. Compare with the canonical order here.

    • Client vs server error: Add "use client" to pages/components that consume hooks.


    11) Minimal “Blocks Gallery” pattern (optional)

    A local gallery lets teammates see every block live:

    • Dev server: pnpm dev → open /blocks

    • Wire the global providers (as above)

    • Add pages:


    12) VibeCoding: prompt scaffolds (copy-paste)

    A) System prompt (one-time context)

    B) Task prompt (generate page + wiring)

    C) Debug prompt (when things break)


    13) Notes for AIs (indexing hints)

    • Important keywords: Trustless Work, Escrow Blocks, single-release, multi-release, providers, WalletProvider, ReactQueryClientProvider, role-based actions, FundEscrowDialog, ApproveMilestoneDialog, ChangeMilestoneStatusDialog, ReleaseEscrowButton, ReleaseMilestoneButton, InitializeEscrowDialog.

    • Do not reorder providers. Quote this rule when suggesting edits.

    • Docs are AI-exportable; searching the docs in natural language is supported.


    14) Appendix — canonical provider order (snippet)

    Source reference for order requirement.


    License to remix

    Use, copy, embed, and ship. If your agent needs more, point it at the full docs hub and API reference. The overall documentation explicitly supports AI export & semantic search.

    Vibe Coding - SDK

    In all of your chat prompts, you should provide the agent with specific document references, including the global content rules and the complete code project. GPT 5 gives the best result

    Global Context

    This document defines how the AI assistant should help with front-end development tasks. The global context below establishes the persona, expertise, and workflow patterns that should be followed in all interactions.

    PROMPTS

    Attach the Global Context and referenced documents to all prompts for clarity and consistency.

    1 - Trustless Work - React Library Setup

    Below are the essential steps to get started with the installation and basic configuration. You should attach these links as docs reference:

    Ensure to set the API Key in a .env file during this step.

    2 - Stellar Wallet Kit

    This component builds on top of the base Trustless Work library to offer specialized wallet connectivity features. You should attach these links as docs reference:

    3 - Initialize Escrow

    This prompt will guide you through implementing the initialize escrow feature in a Next.js application using the Trustless Work library. You should attach these links as docs reference: and

    4 - Save Escrow in Global Store

    This prompt will help you implement a global state management solution to store and access escrow data across your application. You should attach these links as docs reference: and

    5 - Fund Escrow

    This prompt will guide you through implementing the fund escrow feature using the Trustless Work library in your Next.js application. You should attach these links as docs reference: and

    6 - Change Milestone Status

    This prompt will guide you through implementing the functionality to change milestone statuses in an escrow contract using the Trustless Work library. You should attach these links as docs reference: and

    7 - Approve Milestone

    This prompt will guide you through implementing the approve milestone feature in your escrow workflow using the Trustless Work library. You should attach these links as docs reference: and

    8 - Release Funds

    This prompt will guide you through implementing the release funds feature in your escrow workflow using the Trustless Work library. This function allows clients to release funds to freelancers after milestone approval. You should attach these links as docs reference: and

    9 - Start Dispute

    This prompt will guide you through implementing the start dispute feature in your escrow workflow using the Trustless Work library. This functionality allows parties to initiate a dispute process when disagreements arise. You should attach these links as docs reference: and

    10 - Resolve Dispute

    This prompt will guide you through implementing the resolve dispute feature in your escrow workflow using the Trustless Work library. This advanced functionality allows the arbiter to make a final decision on disputes, allocating funds accordingly. You should attach these links as docs reference: and

    Developer Quickstart

    This guide is your all-in-one resource to integrate, deploy, and extend Trustless Work. Whether you’re a developer, a product team, or seeding an AI agent — this page gives you everything.

    Hello! This page is optimized to be exported and loaded to your AI Agent of choice! you can also use ours in the Search Bar!

    🚀 What Is Trustless Work?

    Trustless Work is an Escrow-as-a-Service (EaaS) platform designed for the stablecoin economy. It lets you securely hold funds in non-custodial smart contracts until milestones are completed and approved.

    ---
    alwaysApply: true
    ---
    You are a **Senior Front-End Developer** and **Expert** in:
    - ReactJS, NextJS, JavaScript, TypeScript
    - TailwindCSS, Shadcn, Radix UI
    - HTML, CSS, and modern UI/UX best practices
    
    You are methodical, precise, and a master at reasoning through complex requirements. You always provide correct, DRY, bug-free, production-ready code.
    
    ## General Rules
    - Follow the user’s requirements **exactly** as stated.
    - Think step-by-step:  
      1. **Analyze** the requirement.  
      2. **Write detailed pseudocode** describing the implementation plan.  
      3. **Confirm** the plan (if asked).  
      4. **Write complete code** that matches the plan.  
    - Never guess. If something is unclear, ask for clarification.
    - If an external library is mentioned, always refer to its official documentation before implementation.
    - Always ensure the final code is fully functional, with no placeholders, `TODO`s, or missing parts.
    - Prefer readability over performance.
    - Use best practices for React & Next.js development.
    - Do not use cd in order to access to determinate root, neither use &&, | or something like that in shell actions.
    - Do not verify the build during the Trustless Work implementations.
    - In each npm i, the name of the dependency must be enclosed in double quotation marks (“”).
    - Do not ask for 2 o more ways to implement, just do it the best way possible.
    - Do not plan or ask for steps; just implement the code without asking questions.
    
    ## Trustless Work Integration Context
    When working with Trustless Work:
    - Documentation (I'll provide you the docs in the cursor docs management):  
      - React Library → <https://docs.trustlesswork.com/trustless-work/react-library>  
      - Wallet Kit → <https://docs.trustlesswork.com/trustless-work/developer-resources/stellar-wallet-kit-quick-integration>  
      - Types → <https://docs.trustlesswork.com/trustless-work/developer-resources/types>  
    - Ensure proper installation and configuration before usage.
    - Use provided Types from the documentation when applicable.
    - Follow the API and component usage exactly as described in the docs.
    - Do not use any, instead always you must search for the Trustless Work entities.
    
    ## Code Implementation Guidelines
    - Use **TailwindCSS classes** for styling; avoid plain CSS.
    - For conditional classes, prefer `clsx` or similar helper functions over ternary operators in JSX.
    - Use **descriptive** variable, function, and component names.  
      - Event handlers start with `handle` (e.g., `handleClick`, `handleSubmit`).
    - Prefer **const** arrow functions with explicit type annotations over `function` declarations.
    - Always include all necessary imports at the top.
    - Use early returns to improve code clarity.
    
    ## Verification Before Delivery
    Before finalizing:
    1. Check that all required imports are present.
    2. Ensure the code compiles in a Next.js 14+ environment.
    3. Confirm that Tailwind and Shadcn styles render correctly.
    4. Verify that Trustless Work components or hooks are properly initialized.
    5. Ensure TypeScript types are correct and there are no type errors.
    
    for multi).
  • Optional: Dispute/Resolve flows (type-specific components).

  • Asset errors: Ensure the USDC trustline is added in Freighter for the correct network.

  • Read-only works; writes fail: Missing or invalid NEXT_PUBLIC_API_KEY, wrong role, or wallet not on the right network.

  • /blocks/wallet-button
  • /blocks/escrows-by-role/cards|table

  • /blocks/escrows-by-signer/cards|table

  • /blocks/single-release/* (init/update/dispute/resolve/release)

  • /blocks/multi-release/* (init/update/dispute/resolve/release-milestone)

  • /blocks/single-multi-release/* (fund/approve/change-status)

  • React Library TW
    Stellar Wallet Kit
    React Library TW
    Types TW
    React Library TW
    Types TW
    React Library TW
    Types TW
    React Library TW
    Types TW
    React Library TW
    Types TW
    React Library TW
    Types TW
    React Library TW
    Types TW
    React Library TW
    Types TW
    npx create-next-app@latest tw-blocks-dapp --typescript --tailwind
    cd tw-blocks-dapp
    npm install @trustless-work/blocks
    npx trustless-work init
    # Required for authenticated calls (when you start acting)
    NEXT_PUBLIC_API_KEY=your_api_key_here
    // app/layout.tsx
    import "./globals.css";
    import { ReactQueryClientProvider } from "@/components/tw-blocks/providers/ReactQueryClientProvider";
    import { TrustlessWorkProvider } from "@/components/tw-blocks/providers/TrustlessWork";
    import { WalletProvider } from "@/components/tw-blocks/wallet-kit/WalletProvider";
    import { EscrowProvider } from "@/components/tw-blocks/providers/EscrowProvider";
    import { EscrowDialogsProvider } from "@/components/tw-blocks/providers/EscrowDialogsProvider";
    import { EscrowAmountProvider } from "@/components/tw-blocks/providers/EscrowAmountProvider";
    
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en">
          <body>
            <ReactQueryClientProvider>
              <TrustlessWorkProvider>
                <WalletProvider>
                  <EscrowProvider>
                    <EscrowDialogsProvider>
                      <EscrowAmountProvider>
                        {children}
                      </EscrowAmountProvider>
                    </EscrowDialogsProvider>
                  </EscrowProvider>
                </WalletProvider>
              </TrustlessWorkProvider>
            </ReactQueryClientProvider>
          </body>
        </html>
      );
    }
    # Core glue
    npx trustless-work add wallet-kit
    npx trustless-work add providers
    npx trustless-work add tanstack
    npx trustless-work add helpers
    npx trustless-work add handle-errors
    
    # Lifecycle blocks
    npx trustless-work add escrows/single-release
    npx trustless-work add escrows/multi-release
    npx trustless-work add escrows/single-multi-release
    
    # Listings
    npx trustless-work add escrows/escrows-by-role/cards
    # optional:
    # npx trustless-work add escrows/escrows-by-role/table
    # npx trustless-work add escrows/escrows-by-signer/cards
    # npx trustless-work add escrows/escrows-by-signer/table
    // app/page.tsx
    "use client";
    
    import { WalletButton } from "@/components/tw-blocks/wallet-kit/WalletButtons";
    import { InitializeEscrowDialog } from "@/components/tw-blocks/escrows/single-release/initialize-escrow/dialog/InitializeEscrow";
    import { EscrowsByRoleCards } from "@/components/tw-blocks/escrows/escrows-by-role/cards/EscrowsCards";
    
    export default function Home() {
      return (
        <div className="container mx-auto py-8">
          <header className="flex justify-between items-center mb-8">
            <h2 className="text-2xl font-bold">Trustless Work Demo</h2>
            <WalletButton />
          </header>
    
          <div className="flex justify-end mb-4">
            <InitializeEscrowDialog />
          </div>
    
          <EscrowsByRoleCards />
        </div>
      );
    }
    // escrows/escrows-by-role/details/Actions.tsx (example)
    return (
      <div className="flex items-start justify-start flex-col gap-2 w-full">
        {/* Render actions conditionally by flags + roles */}
        {hasConditionalButtons && (
          <div className="flex flex-col gap-2 w/full">
            {/* Single-release only */}
            {/* {shouldShowEditButton && <UpdateEscrowDialog />} */}
            {/* {shouldShowDisputeButton && <DisputeEscrowButton />} */}
            {/* {shouldShowResolveButton && <ResolveDisputeDialog />} */}
            {/* {shouldShowReleaseFundsButton && <ReleaseEscrowButton />} */}
          </div>
        )}
        <FundEscrowDialog /> {/* shared single/multi */}
      </div>
    );
    // escrows/escrows-by-role/details/Actions.tsx (imports)
    import { UpdateEscrowDialog } from "../../single-release/update-escrow/dialog/UpdateEscrow";
    /* import { UpdateEscrowDialog as UpdateEscrowDialogMultiRelease } from "../../multi-release/update-escrow/dialog/UpdateEscrow"; */
    import { FundEscrowDialog } from "../../single-multi-release/fund-escrow/dialog/FundEscrow";
    import { DisputeEscrowButton } from "../../single-release/dispute-escrow/button/DisputeEscrow";
    import { ResolveDisputeDialog } from "../../single-release/resolve-dispute/dialog/ResolveDispute";
    import { ReleaseEscrowButton } from "../../single-release/release-escrow/button/ReleaseEscrow";
    You are an expert Next.js + Trustless Work Escrow Blocks engineer.
    Follow these rules:
    - Use files and paths exactly as provided here.
    - Keep provider order identical to the guide.
    - Prefer code that compiles with no TODOs.
    - When an escrow type is single vs multi, import the matching actions.
    Build a Next.js page that:
    1) shows a WalletButton in the header,
    2) renders InitializeEscrowDialog (single-release),
    3) lists EscrowsByRoleCards with working details,
    4) enables FundEscrowDialog and ReleaseEscrowButton for single-release.
    
    Use the provider order from app/layout.tsx and imports from:
    /components/tw-blocks/...
    
    If a component uses hooks, ensure "use client" at top.
    Given provider order MUST be:
    ReactQueryClientProvider > TrustlessWorkProvider > WalletProvider > EscrowProvider > EscrowDialogsProvider > EscrowAmountProvider.
    
    Identify why EscrowsByRoleCards actions are disabled. Check:
    - wallet role vs escrow role,
    - escrow funded/approved flags,
    - correct single vs multi action imports,
    - USDC trustline present in Freighter (Testnet).
    Propose exact code edits.
    <ReactQueryClientProvider>
      <TrustlessWorkProvider>
        <WalletProvider>
          <EscrowProvider>
            <EscrowDialogsProvider>
              <EscrowAmountProvider>
                {children}
              </EscrowAmountProvider>
            </EscrowDialogsProvider>
          </EscrowProvider>
        </WalletProvider>
      </TrustlessWorkProvider>
    </ReactQueryClientProvider>
    Configure the initial setup to use the Trustless Work React library in a Next.js app.
    
    - Install the required dependency.
    - Set up the provider at the app root.
    - Ensure all imports are correct.
    - Use TypeScript if types are available in the documentation.
    Configure the initial setup for the Stellar Wallet Kit in a Next.js app based on the documentation, please follow their indications.
    
    - Install the required dependency.
    - Ensure all imports are correct.
    - Use TypeScript if types are provided.
    - Make sure the wallet is ready to be used across the app.
    - Implement the wallet hooks by using buttons.
    Implement the useInitializeEscrow function from the Trustless Work React library in our Next.js app.
    
    - Use mock data for the payload values, except for the fields explicitly provided below.
    - Add a button that initializes the escrow when clicked.
    - Use multi-release mode.
    - Use this USDC trustline address: CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA
    - Use this decimals value: 10000000
    - For all roles, use the wallet address of the currently connected user.
    - The payload type must be InitializePayload (as defined in the official payloads documentation: <https://docs.trustlesswork.com/trustless-work/developer-resources/types>).
    - After sendTransaction returns, display the contractId on screen with a clickable link to view it in Stellar Viewer.
    - Set platformFee to 4.
    - Ensure TypeScript types are correct.
    - Make sure in the submit function, do these 3 steps always: execute function from tw, sign transaction with wallet and sendTransaction.
    Update the useInitializeEscrow implementation to handle the full response from sendTransaction.
    
    - After calling sendTransaction, store the returned escrow object and the contractId in a React Context.
    - Do not fetch the escrow from anywhere else; only use the one returned directly from sendTransaction.
    - Example: const response = await sendTransaction(...); // response contains: { status, message, contractId, escrow }
    - Create a section in the UI to visually display all the escrow properties, assuming the type is MultiReleaseEscrow.
    - Ensure TypeScript types are correct.
    Implement the useFundEscrow hook from the Trustless Work React library to fund an existing escrow contract.
    
    - Use the contractId stored in the React Context from the previous step.
    - Use multi-release mode.
    - Add a button that funds the escrow when clicked.
    - The payload type must be FundEscrowPayload (as defined in the official payloads documentation).
    - Include proper error handling and loading states.
    - After successful funding, display a success message to the user.
    - Ensure TypeScript types are correct and all imports are present.
    - Display the transaction status and any relevant details returned from the hook.
    - Update the escrow store in context.
    - Do not add extra properties of FundEscrowPayload
    - The amount must be the same number of the escrow amount, which it means that if we have 2 milestone of 5, the amount for the fund will be 10
    - Make sure in the submit function, do these 3 steps always: execute function from tw, sign transaction with wallet and sendTransaction.
    Implement the useChangeMilestoneStatus hook from the Trustless Work React library to update milestone statuses in the multi-release escrow.
    
    - Use the contractId and escrow data stored in the React Context.
    - Use multi-release mode.
    - Add UI components that allow selecting a milestone and changing its status.
    - The payload type must be ChangeMilestoneStatusPayload (as defined in the official documentation).
    - Implement separate buttons for each possible status transition (e.g., "Mark as Completed", "Reject", etc.).
    - Only show status change options that are valid for the current milestone state.
    - Include proper error handling and loading states.
    - After successful status change, update the escrow data in the context.
    - Ensure TypeScript types are correct and all imports are present.
    - Make sure in the submit function, do these 3 steps always: execute function from tw, sign transaction with wallet and sendTransaction.
    Implement the useApproveMilestone hook from the Trustless Work React library to approve milestones in the multi-release escrow.
    
    - Use the contractId and escrow data stored in the React Context.
    - Use multi-release mode.
    - Add UI components that allow selecting a milestone and approving it.
    - The payload type must be ApproveMilestonePayload (as defined in the official documentation).
    - Include proper error handling and loading states.
    - After successful approval, update the escrow data in the context.
    - Ensure TypeScript types are correct and all imports are present.
    - Display a confirmation message after successful approval.
    - Make sure in the submit function, do these 3 steps always: execute function from tw, sign transaction with wallet and sendTransaction.
    Implement the useReleaseFunds hook from the Trustless Work React library to release funds for approved milestones in the multi-release escrow.
    
    - Use the contractId and escrow data stored in the React Context.
    - Use multi-release mode.
    - Add UI components that allow selecting a milestone and releasing funds for it.
    - The payload type must be MultiReleaseReleaseFundsPayload (as defined in the official documentation).
    - Include proper error handling and loading states.
    - After successful fund release, update the escrow data in the context.
    - Ensure TypeScript types are correct and all imports are present.
    - Display a success message after funds are released successfully.
    - Make sure in the submit function, do these 3 steps always: execute function from tw, sign transaction with wallet and sendTransaction.
    Implement the useStartDispute hook from the Trustless Work React library to initiate disputes in the multi-release escrow.
    
    - Use the contractId and escrow data stored in the React Context.
    - Use multi-release mode.
    - Add UI components that allow selecting a milestone and starting a dispute.
    - The payload type must be MultiReleaseStartDisputePayload (as defined in the official documentation).
    - Include proper error handling and loading states.
    - After successfully starting a dispute, update the escrow data in the context.
    - Ensure TypeScript types are correct and all imports are present.
    - Display a confirmation after the dispute has been successfully initiated.
    - Make sure in the submit function, do these 3 steps always: execute function from tw, sign transaction with wallet and sendTransaction.
    Implement the useResolveDispute hook from the Trustless Work React library to resolve disputes in the multi-release escrow.
    
    - Use the contractId and escrow data stored in the React Context.
    - Use multi-release mode.
    - Add UI components that allow selecting a milestone and resolving a dispute.
    - The payload type must be MultiReleaseResolveDisputePayload (as defined in the official documentation).
    - Include proper error handling and loading states.
    - After successfully resolving a dispute, update the escrow data in the context.
    - Ensure TypeScript types are correct and all imports are present.
    - Display a confirmation message after the dispute has been successfully resolved.
    - Make sure in the submit function, do these 3 steps always: execute function from tw, sign transaction with wallet and sendTransaction.

    Use it to:

    • Lock funds with programmable milestone logic

    • Enable transparent fund releases for services, grants, rentals, etc.

    • Automate fund flows using signer-based roles

    🧪 Quickstart — Deploy Your First Escrow

    ▶ Try the Demo — No code required

    Pre-requisites

    • Freighter wallet

    • Testnet USDC + XLM (Get test tokens)

    Step-by-Step

    1. Open the demo app

    2. Fill in escrow details (roles, milestones)

    3. Click deploy → sign the transaction

    4. Send testmet USDC to the escrow contract address

    5. Mark, approve, and release milestones from the UI


    ✍️ 2. Design Your Escrow Lifecycle

    Before deploying, define:

    • Who can mark milestones as done

    • Who must approve work

    • Who can release funds

    • Who can resolve disputes

    → Escrow Roles & Permissions


    📬 API Overview

    Get your API key in our dApp: Request API Key

    The Trustless Work API is your gateway to deploy and manage decentralized smart escrows on the Stellar blockchain using Soroban smart contracts. All interactions return unsigned XDRs, which must be signed client-side using the wallet associated with the correct role.

    📘 Base URL: https://api.trustlesswork.com


    🔨 Deployment

    Method
    Endpoint
    Description

    POST

    /deployer/single-release

    Deploys a single-release escrow

    POST

    /deployer/multi-release

    Deploys a multi-release escrow


    💸 Funding

    Method
    Endpoint
    Description

    POST

    /escrow/{type}/fund-escrow

    Returns XDR to fund an escrow


    ✅ Milestone Handling

    Method
    Endpoint
    Description

    POST

    /escrow/{type}/approve-milestone

    Approve a milestone

    POST

    /escrow/{type}/change-milestone-status

    Mark a milestone as complete/incomplete


    🏁 Finalization

    Method
    Endpoint
    Description

    POST

    /escrow/{type}/release-funds

    Release all funds (single)

    POST

    /escrow/{type}/release-milestone-funds

    Release one milestone (multi)


    ⚠️ Disputes

    Method
    Endpoint
    Description

    POST

    /escrow/{type}/dispute-escrow

    Raise a dispute on a single escrow

    POST

    /escrow/{type}/resolve-dispute

    Resolve a single-release escrow dispute

    POST

    /escrow/{type}/dispute-milestone

    Raise dispute on a milestone

    POST

    /escrow/{type}/resolve-milestone-dispute

    Resolve milestone dispute (multi)


    🔄 Escrow Updates

    Method
    Endpoint
    Description

    POST

    /escrow/{type}/update-escrow

    Update escrow metadata/config


    📊 Query & Tracking

    Method
    Endpoint
    Description

    GET

    /escrow/get-multiple-escrow-balance

    Batch balances for many escrows

    GET

    /helper/get-escrows-by-signer

    Query escrows associated with a signer

    GET

    /helper/get-escrows-by-role

    Query escrows by role assignment


    🧰 Helper Utilities

    Method
    Endpoint
    Description

    POST

    /helper/set-trustline

    Enable escrow wallet to accept a token

    POST

    /helper/send-transaction

    Submit signed XDR to Stellar


    📌 For full Swagger documentation, visit: https://dev.api.trustlesswork.com/docs

    All write actions must be signed by the wallet that holds the corresponding escrow role (marker, approver, releaser, resolver).

    🧠 React SDK Integration

    NPM: @trustless-work/escrow

    Provider Setup

    Wrap your app:

    Once wrapped, you can use the SDK's escrow hooks and mutation functions across your app.


    🔑 Wallets & Passkeys

    Every role (marker, approver, releaser, etc.) needs a Stellar-Soroban compatible wallet.

    Supported wallets:

    • Freighter

    • Passkey Wallets (biometric, contract-based)


    🔁 Escrow Lifecycle Explained

    1. Initiation – Define roles, asset, and milestones

    2. Funding – Deposit stablecoins (USDC) into the contract

    3. Milestone Marked – Provider marks progress

    4. Approval – Client or approver signs off

    5. Release – Funds are released

    6. Dispute – Optional: Resolver steps in

    Each action requires a signature from the assigned wallet address.


    🧩 Roles & Permissions

    Role
    Description

    Marker

    Marks milestones as completed

    Approver

    Approves milestone completion

    Releaser

    Signs off final release of funds

    Resolver

    Can override flow in case of dispute

    Receiver

    Gets the released funds

    Platform Address

    Receives a fee (optional, % of each release)


    📐 Escrow Schema Reference (for agents & devs)

    Shared Fields

    Key
    Type
    Description

    engagementId

    string

    Unique identifier for the escrow

    title

    string

    Name of the escrow

    description

    string

    Description of the escrow's function

    roles

    object

    Role assignments: marker, approver, etc.

    Milestone Object (for multi-release only)

    Field
    Type
    Description

    description

    string

    What the milestone represents

    status

    string

    Status: approved, in_dispute, etc.

    amount

    number

    Amount released upon approval

    evidence

    string

    (Optional) Supporting proof

    Trustline Object

    Field
    Type
    Description

    address

    string

    Token issuer or address


    🛠 Developer Tools & Links

    • 🔗 API Docs

    • 🧪 Demo dApp

    • Github https://github.com/Trustless-Work

    • 📦 React SDK (NPM)

    • 📖


    🤝 Join the Ecosystem

    • 🧵 Twitter/X: @trustlesswork

    • 🌐 Docs Hub: docs.trustlesswork.com


    Built for the stablecoin economy. Open-source. Developer-first.

    Let us know what you’re building!

    Stellar Wallet Kit - Quick Integration

    Building a Stellar Wallet Management System

    This guide will walk you through creating a comprehensive wallet management system for Stellar blockchain integration in your React/Next.js application. The system provides secure wallet connection, disconnection, and state management with persistence.

    Overview

    The system consists of three main components:

    1. Wallet Provider - Global state management for wallet information

    2. Wallet Hook - Business logic for wallet operations

    3. Wallet Kit Configuration - Stellar blockchain integration setup

    Step 1: Install Dependencies

    First, install the required Stellar Wallet Kit package:

    Step 2: Configure the Stellar Wallet Kit

    Create a configuration file that sets up the Stellar Wallet Kit with support for multiple wallet types:

    Step 3: Create the Wallet Context Provider

    The Wallet Provider manages global wallet state and persists information in localStorage for better user experienc

    Step 4: Create the Wallet Hook

    The wallet hook encapsulates all the business logic for wallet operations, providing a clean interface for components:

    Step 5: Integrate the Provider in Your App

    Wrap your application with the WalletProvider to enable wallet state management throughout your app:

    Step 6: Use the Wallet Hook in Components

    Now you can use the wallet functionality in any component. Here's an example of a wallet connection button:

    Key Features and Benefits

    State Persistence

    • Wallet information is automatically saved to localStorage

    • Users don't need to reconnect their wallet every time they visit your app

    • Seamless user experience across browser sessions

    Multi-Wallet Support

    • Supports multiple Stellar wallet types (Freighter, Albedo, etc.)

    • Easy to add new wallet modules as they become available

    • Users can choose their preferred wallet

    Error Handling

    • Comprehensive error handling for wallet operations

    • Graceful fallbacks when wallet operations fail

    • Easy to extend with custom error notifications

    Type Safety

    • Full TypeScript support with proper type definitions

    • IntelliSense support for better development experience

    • Compile-time error checking for wallet operations

    Context-Based Architecture

    • Clean separation of concerns between state management and business logic

    • Easy to access wallet state from any component

    • Centralized wallet state management

    Configuration Options

    Network Selection

    • TESTNET: Use during development and testing

    • MAINNET: Use for production applications

    npm i @trustless-work/escrow
    
    import {
      TrustlessWorkConfig,
      development, // or mainNet
    } from "@trustless-work/escrow";
    
    export function TrustlessWorkProvider({ children }) {
      const apiKey = process.env.NEXT_PUBLIC_API_KEY || "";
      return (
        <TrustlessWorkConfig baseURL={development} apiKey={apiKey}>
          {children}
        </TrustlessWorkConfig>
      );
    }
    
    export default function RootLayout({ children }) {
      return (
        <html lang="en">
          <body>
            <TrustlessWorkProvider>
              {children}
            </TrustlessWorkProvider>
          </body>
        </html>
      );
    }
    

    platformFee

    number

    % fee for the platform

    trustline

    object

    Token type (e.g., USDC, XLM) and decimals

    flags

    object

    Includes disputed, resolved, approved

    Wallet Setup Guide
    npm install @creit.tech/stellar-wallets-kit
    import {
      StellarWalletsKit,
      WalletNetwork,
      FREIGHTER_ID,
      AlbedoModule,
      FreighterModule,
    } from "@creit.tech/stellar-wallets-kit";
    
    /**
     * Main configuration for Stellar Wallet Kit
     * This kit supports multiple wallet types including Freighter and Albedo
     * Configure for TESTNET during development and MAINNET for production
     */
    export const kit: StellarWalletsKit = new StellarWalletsKit({
      network: WalletNetwork.TESTNET,
      selectedWalletId: FREIGHTER_ID,
      modules: [new FreighterModule(), new AlbedoModule()],
    });
    
    /**
     * Interface for transaction signing parameters
     */
    interface signTransactionProps {
      unsignedTransaction: string;
      address: string;
    }
    
    /**
     * Sign a Stellar transaction using the connected wallet
     * This function handles the signing process and returns the signed transaction
     * 
     * @param unsignedTransaction - The XDR string of the unsigned transaction
     * @param address - The wallet address that will sign the transaction
     * @returns Promise<string> - The signed transaction XDR
     */
    export const signTransaction = async ({
      unsignedTransaction,
      address,
    }: signTransactionProps): Promise<string> => {
      const { signedTxXdr } = await kit.signTransaction(unsignedTransaction, {
        address,
        networkPassphrase: WalletNetwork.TESTNET,
      });
    
      return signedTxXdr;
    };
    "use client";
    
    import {
      createContext,
      useContext,
      useState,
      useEffect,
      ReactNode,
    } from "react";
    
    /**
     * Type definition for the wallet context
     * Contains wallet address, name, and functions to manage wallet state
     */
    type WalletContextType = {
      walletAddress: string | null;
      walletName: string | null;
      setWalletInfo: (address: string, name: string) => void;
      clearWalletInfo: () => void;
    };
    
    /**
     * Create the React context for wallet state management
     */
    const WalletContext = createContext<WalletContextType | undefined>(undefined);
    
    /**
     * Wallet Provider component that wraps the application
     * Manages wallet state and provides wallet information to child components
     * Automatically loads saved wallet information from localStorage on initialization
     */
    export const WalletProvider = ({ children }: { children: ReactNode }) => {
      const [walletAddress, setWalletAddress] = useState<string | null>(null);
      const [walletName, setWalletName] = useState<string | null>(null);
    
      /**
       * Load saved wallet information from localStorage when the component mounts
       * This ensures the wallet state persists across browser sessions
       */
      useEffect(() => {
        const storedAddress = localStorage.getItem("walletAddress");
        const storedName = localStorage.getItem("walletName");
    
        if (storedAddress) setWalletAddress(storedAddress);
        if (storedName) setWalletName(storedName);
      }, []);
    
      /**
       * Set wallet information and save it to localStorage
       * This function is called when a wallet is successfully connected
       * 
       * @param address - The wallet's public address
       * @param name - The name/identifier of the wallet (e.g., "Freighter", "Albedo")
       */
      const setWalletInfo = (address: string, name: string) => {
        setWalletAddress(address);
        setWalletName(name);
        localStorage.setItem("walletAddress", address);
        localStorage.setItem("walletName", name);
      };
    
      /**
       * Clear wallet information and remove it from localStorage
       * This function is called when disconnecting a wallet
       */
      const clearWalletInfo = () => {
        setWalletAddress(null);
        setWalletName(null);
        localStorage.removeItem("walletAddress");
        localStorage.removeItem("walletName");
      };
    
      return (
        <WalletContext.Provider
          value={{ walletAddress, walletName, setWalletInfo, clearWalletInfo }}
        >
          {children}
        </WalletContext.Provider>
      );
    };
    
    /**
     * Custom hook to access the wallet context
     * Provides wallet state and functions to components
     * Throws an error if used outside of WalletProvider
     */
    export const useWalletContext = () => {
      const context = useContext(WalletContext);
      if (!context) {
        throw new Error("useWalletContext must be used within WalletProvider");
      }
      return context;
    };
    import { kit } from "@/config/wallet-kit";
    import { useWalletContext } from "@/providers/wallet.provider";
    import { ISupportedWallet } from "@creit.tech/stellar-wallets-kit";
    
    /**
     * Custom hook that provides wallet connection and disconnection functionality
     * Integrates with the Stellar Wallet Kit and manages wallet state through context
     */
    export const useWallet = () => {
      // Get wallet management functions from the context
      const { setWalletInfo, clearWalletInfo } = useWalletContext();
    
      /**
       * Connect to a Stellar wallet using the Wallet Kit
       * Opens a modal for wallet selection and handles the connection process
       * Automatically sets wallet information in the context upon successful connection
       */
      const connectWallet = async () => {
        await kit.openModal({
          modalTitle: "Connect to your favorite wallet",
          onWalletSelected: async (option: ISupportedWallet) => {
            // Set the selected wallet as the active wallet
            kit.setWallet(option.id);
    
            // Get the wallet address and name
            const { address } = await kit.getAddress();
            const { name } = option;
    
            // Store wallet information in the context and localStorage
            setWalletInfo(address, name);
          },
        });
      };
    
      /**
       * Disconnect from the current wallet
       * Clears wallet information from the context and localStorage
       * Disconnects the wallet from the Stellar Wallet Kit
       */
      const disconnectWallet = async () => {
        await kit.disconnect();
        clearWalletInfo();
      };
    
      /**
       * Handle wallet connection with error handling
       * Wraps the connectWallet function in a try-catch block for better error management
       */
      const handleConnect = async () => {
        try {
          await connectWallet();
        } catch (error) {
          console.error("Error connecting wallet:", error);
          // You can add additional error handling here, such as showing user notifications
        }
      };
    
      /**
       * Handle wallet disconnection with error handling
       * Wraps the disconnectWallet function in a try-catch block for better error management
       */
      const handleDisconnect = async () => {
        try {
          await disconnectWallet();
        } catch (error) {
          console.error("Error disconnecting wallet:", error);
          // You can add additional error handling here, such as showing user notifications
        }
      };
    
      return {
        connectWallet,
        disconnectWallet,
        handleConnect,
        handleDisconnect,
      };
    };
    import { WalletProvider } from "@/providers/wallet.provider";
    
    export default function RootLayout({
      children,
    }: {
      children: React.ReactNode;
    }) {
      return (
        <html lang="en">
          <body>
            <WalletProvider>
              {children}
            </WalletProvider>
          </body>
        </html>
      );
    }
    import { useWallet } from "@/hooks/wallet.hook";
    import { useWalletContext } from "@/providers/wallet.provider";
    
    /**
     * Wallet connection/disconnection button component
     * Shows different states based on wallet connection status
     */
    export const WalletButton = () => {
      const { handleConnect, handleDisconnect } = useWallet();
      const { walletAddress, walletName } = useWalletContext();
    
      // If wallet is connected, show disconnect option
      if (walletAddress) {
        return (
          <div className="flex items-center gap-4">
            <div className="text-sm">
              <p className="font-medium">Connected: {walletName}</p>
              <p className="text-gray-500">{walletAddress}</p>
            </div>
            <button 
              onClick={handleDisconnect}
              className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
            >
              Disconnect
            </button>
          </div>
        );
      }
    
      // If wallet is not connected, show connect option
      return (
        <button 
          onClick={handleConnect}
          className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
        >
          Connect Wallet
        </button>
      );
    };
    get
    Authorizations
    Query parameters
    Responses
    200

    Collection of objects containing information on the balance of the requested addresses

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500Error
    application/json
    get
    /helper/get-multiple-escrow-balance
    get
    Authorizations
    Query parameters
    contractIdsstring[]Required

    Array of contract IDs to query

    Example: [CB25FW...,CBHEQBV...]
    isActivebooleanOptional

    Is the escrow active

    validateOnChainbooleanOptional

    When set to true, the endpoint will verify each escrow’s data against the blockchain to ensure accuracy and integrity. Enabling this adds an extra security layer but may increase the response time slightly due to the additional validation step

    Responses
    200

    Collection of data containing information from different scans requested by a signatory, role or contract ids.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500Error
    application/json
    get
    /helper/get-escrow-by-contract-ids
    get
    Authorizations
    Query parameters
    signerstringOptional

    Address of the user who signed the transaction to create escrow

    Example: GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    rolestringOptional

    Role in escrow

    Example: approver
    roleAddressstringOptional

    Address of the user in the specified role

    Example: GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    statusstringOptional

    Status of the escrow

    Example: pending
    typestring · enumOptional

    Type of the escrow

    Example: single_release | multi_releasePossible values:
    engagementIdstringOptional

    Engagement ID of the escrow

    Example: ENG-001
    titlestringOptional

    Title of the escrow

    Example: Website redesign
    isActivebooleanOptional

    Is the escrow active

    validateOnChainbooleanOptional

    When set to true, the endpoint will verify each escrow’s data against the blockchain to ensure accuracy and integrity. Enabling this adds an extra security layer but may increase the response time slightly due to the additional validation step

    startDatestringOptional

    Starting date range for filtering

    Example: 2023-06-20 (YYYY-MM-DD)
    minAmountnumberOptional

    Minimum amount for filtering

    Example: 100
    maxAmountnumberOptional

    Maximum amount for filtering

    Example: 1000
    orderBystring · enumOptional

    Field to order the results by

    Default: createdAtExample: createdAt | updatedAt | amountPossible values:
    orderDirectionstring · enumOptional

    Direction to order the results

    Default: descExample: asc | descPossible values:
    pagenumberOptional

    Page number for pagination

    Example: 1
    pageSizenumberOptional

    Number of items per page

    Default: 8Example: 8
    contractIDstringOptional

    Contract Id of the deployed escrow

    Example: CBHEQBV...
    endDatestringOptional

    Ending date range for filtering

    Example: 2023-06-22 (YYYY-MM-DD)
    Responses
    200

    Collection of data containing information from different scans requested by a signatory, role or contract id.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500Error
    application/json
    get
    /helper/get-escrows-by-role
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    milestoneIndexstringRequired

    Position that identifies the milestone within the group of milestones in the escrow

    Example: 1
    signerstringRequired

    Entity that signs the transaction that deploys and initializes the escrow

    Example: GSIGN...XYZ
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Escrow not found
    • Escrow initialized without milestone
    • Invalid milestone index
    • This milestone is already released
    • This milestone is already resolved
    • Milestone already in dispute
    • You are not authorized to change the dispute flag
    • The dispute resolver cannot be the one to raise a dispute on a milestone
    • An unexpected error occurred
    application/json
    post
    /escrow/multi-release/dispute-milestone
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    milestoneIndexstringRequired

    Position that identifies the milestone within the group of milestones in the escrow

    Example: 1
    approverstringRequired

    Address of the entity requiring the service

    Example: GCLIENT...XYZ
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Escrow not found
    • Only the approver can change milestone flag
    • You cannot approve a milestone that has already been approved previously
    • The milestone status cannot be empty
    • Escrow initialized without milestone
    • Invalid milestone index
    • An unexpected error occurred
    application/json
    post
    /escrow/multi-release/approve-milestone
    get
    Authorizations
    Query parameters
    signerstringOptional

    Address of the user who signed the transaction to create escrow

    Example: GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    rolestringOptional

    Role in escrow

    Example: approver
    roleAddressstringOptional

    Address of the user in the specified role

    Example: GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    statusstringOptional

    Status of the escrow

    Example: pending
    typestring · enumOptional

    Type of the escrow

    Example: single_release | multi_releasePossible values:
    engagementIdstringOptional

    Engagement ID of the escrow

    Example: ENG-001
    titlestringOptional

    Title of the escrow

    Example: Website redesign
    isActivebooleanOptional

    Is the escrow active

    validateOnChainbooleanOptional

    When set to true, the endpoint will verify each escrow’s data against the blockchain to ensure accuracy and integrity. Enabling this adds an extra security layer but may increase the response time slightly due to the additional validation step

    startDatestringOptional

    Starting date range for filtering

    Example: 2023-06-20 (YYYY-MM-DD)
    minAmountnumberOptional

    Minimum amount for filtering

    Example: 100
    maxAmountnumberOptional

    Maximum amount for filtering

    Example: 1000
    orderBystring · enumOptional

    Field to order the results by

    Default: createdAtExample: createdAt | updatedAt | amountPossible values:
    orderDirectionstring · enumOptional

    Direction to order the results

    Default: descExample: asc | descPossible values:
    pagenumberOptional

    Page number for pagination

    Example: 1
    pageSizenumberOptional

    Number of items per page

    Default: 8Example: 8
    contractIDstringOptional

    Contract Id of the deployed escrow

    Example: CBHEQBV...
    endDatestringOptional

    Ending date range for filtering

    Example: 2023-06-22 (YYYY-MM-DD)
    Responses
    200

    Collection of data containing information from different scans requested by a signatory, role or contract id.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500Error
    application/json
    get
    /helper/get-escrows-by-signer
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    disputeResolverstringRequired

    Address of the user defined to resolve disputes in an escrow

    Example: GDISPUTE...XYZ
    milestoneIndexstringRequired

    Position that identifies the milestone within the group of milestones in the escrow

    Example: 1
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Escrow not found
    • Invalid milestone index
    • None of the amounts to be transferred should be less or equal than 0
    • Only the dispute resolver can execute this function
    • This milestone is already released
    • This milestone is already resolved
    • Milestone not in dispute
    • The total funds to resolve the dispute must not exceed the amount defined for this milestone
    • Insufficient funds for resolution
    • The total amount to be transferred cannot be zero
    • An unexpected error occurred
    application/json
    post
    /escrow/multi-release/resolve-milestone-dispute
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: ENG12345
    signerstringRequired

    Entity that signs the transaction that deploys and initializes the escrow

    Example: GSIGN...XYZ
    amountstringRequired

    Amount to transfer to the escrow contract

    Example: 100
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Amount cannot be equal to or less than zero
    • The provided escrow properties do not match the stored escrow
    • An unexpected error occurred
    application/json
    post
    /escrow/single-release/fund-escrow
    put
    Authorizations
    Body
    txHashstringRequired

    Transaction hash

    Example: b0e61d4...f1cb2d29
    Responses
    200

    Returns the escrow object that was just saved/updated in the indexer and the success or failure of the response.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500Error
    application/json
    put
    /indexer/update-from-txhash
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    milestoneIndexstringRequired

    milestone within the group of milestones in the escrow

    Example: 1
    newEvidencestringRequired

    New value for the evidence property within the escrow milestone

    Example: Evidence
    newStatusstringRequired

    New value for the status property within the escrow milestone

    Example: Completed
    serviceProviderstringRequired

    Address of the entity providing the service

    Example: Completed
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Escrow not found
    • Only the service provider can change milestone status
    • Invalid milestone index
    • Escrow initialized without milestone
    • An unexpected error occurred
    application/json
    post
    /escrow/multi-release/change-milestone-status
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    milestoneIndexstringRequired

    milestone within the group of milestones in the escrow

    Example: 1
    newEvidencestringRequired

    New value for the evidence property within the escrow milestone

    Example: Evidence
    newStatusstringRequired

    New value for the status property within the escrow milestone

    Example: Completed
    serviceProviderstringRequired

    Address of the entity providing the service

    Example: Completed
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Escrow not found
    • Only the service provider can change milestone status
    • Invalid milestone index
    • Escrow initialized without milestone
    • An unexpected error occurred
    application/json
    post
    /escrow/single-release/change-milestone-status
    post
    Authorizations
    Body
    signedXdrstringRequired

    The sign's hash. This come from the wallet

    Example: AAAAAgAAAAB...
    Responses
    200

    The transaction has been successfully sent to the Stellar network

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500Error
    application/json
    post
    /helper/send-transaction
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    releaseSignerstringRequired

    Address of the user in charge of releasing the escrow funds to the receiver

    Example: GREL...XYZ
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • The escrow funds have been released
    • This escrow is already resolved
    • Only the release signer can release the escrow earnings
    • Escrow initialized without milestone
    • The escrow must be completed to release earnings
    • Escrow has been opened for dispute resolution
    • Escrow not found
    • An unexpected error occurred
    application/json
    post
    /escrow/single-release/release-funds
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: ENG12345
    signerstringRequired

    Entity that signs the transaction that deploys and initializes the escrow

    Example: GSIGN...XYZ
    amountstringRequired

    Amount to transfer to the escrow contract

    Example: 100
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Amount cannot be equal to or less than zero
    • The provided escrow properties do not match the stored escrow
    • An unexpected error occurred
    application/json
    post
    /escrow/multi-release/fund-escrow
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    disputeResolverstringRequired

    Address of the user defined to resolve disputes in an escrow

    Example: GDISPUTE...XYZ
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Only the dispute resolver can execute this function
    • None of the amounts to be transferred should be less or equal than 0
    • Escrow not in dispute
    • Insufficient funds for resolution
    • The sum of distributions must equal the current escrow balance when resolving an escrow dispute
    • The total amount to be distributed cannot be equal to zero
    • Escrow not found
    • An unexpected error occurred
    application/json
    post
    /escrow/single-release/resolve-dispute
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    disputeResolverstringRequired

    Address of the user defined to resolve disputes in an escrow

    Example: GDISPUTE...XYZ
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Escrow not found
    • None of the amounts to be transferred should be less or equal than 0
    • Only the dispute resolver can execute this function
    • All milestones must be released or dispute-resolved before withdrawing remaining funds
    • The total amount to be transferred cannot be zero
    • Insufficient funds for resolution
    • An unexpected error occurred
    application/json
    post
    /escrow/multi-release/withdraw-remaining-funds
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    releaseSignerstringRequired

    Address of the user in charge of releasing the escrow funds to the receiver

    Example: GAPPROVER1234567890...
    milestoneIndexstringRequired

    Position that identifies the milestone within the group of milestones in the escrow

    Example: 1
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • This milestone is already released
    • This escrow is already resolved
    • Only the release signer can release the escrow funds
    • Escrow initialized without milestone
    • The milestone must be completed to release funds
    • Milestone has been opened for dispute resolution
    • Invalid milestone index
    • The escrow balance must be equal to the amount of earnings defined for the escrow
    • Escrow not found
    • An unexpected error occurred
    application/json
    post
    /escrow/multi-release/release-milestone-funds
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    signerstringRequired

    Entity that signs the transaction that deploys and initializes the escrow

    Example: GSIGN...XYZ
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Escrow not found
    • Escrow already in dispute
    • You are not authorized to change the dispute flag
    • The dispute resolver cannot dispute the escrow
    • An unexpected error occurred
    application/json
    post
    /escrow/single-release/dispute-escrow
    post
    Authorizations
    Body
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    milestoneIndexstringRequired

    Position that identifies the milestone within the group of milestones in the escrow

    Example: 1
    approverstringRequired

    Address of the entity requiring the service

    Example: GCLIENT...XYZ
    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Escrow not found
    • Only the approver can change milestone flag
    • You cannot approve a milestone that has already been approved previously
    • The milestone status cannot be empty
    • Escrow initialized without milestone
    • Invalid milestone index
    • An unexpected error occurred
    application/json
    post
    /escrow/single-release/approve-milestone
    post
    Authorizations
    Body
    signerstringRequired

    Entity that signs the transaction that deploys and initializes the escrow

    Example: GABC...XYZ
    engagementIdstringRequired

    Unique identifier for the escrow

    Example: ENG12345
    titlestringRequired

    Name of the escrow

    Example: Escrow Test
    descriptionstringRequired

    Text describing the function of the escrow

    Example: Escrow Test description
    rolesobject[]Required

    Roles that make up the escrow structure

    amountnumberRequired

    Amount to be transferred upon completion of escrow milestones

    Example: 1000
    platformFeenumberRequired

    Commission that the platform will receive when the escrow is completed

    Example: 5
    milestonesobject[]Required

    Objectives to be completed to define the escrow as completed. (In this case it is not necessary to send the properties “approvedFlag” and “status” inside the objects of these milestones)

    trustlineobject[]Required

    Information on the trustline that will manage the movement of funds in escrow

    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Amount cannot be zero
    • Escrow already initialized
    • The platform fee cannot exceed 99%
    • Escrow initialized without milestone
    • Cannot define more than 50 milestones in an escrow
    • All flags (approved, disputed, released) must be false in order to execute this function
    • An unexpected error occurred
    application/json
    post
    /deployer/single-release
    post
    Authorizations
    Body
    signerstringRequired

    Entity that signs the transaction that deploys and initializes the escrow

    Example: GABC...XYZ
    engagementIdstringRequired

    Unique identifier for the escrow

    Example: ENG12345
    titlestringRequired

    Name of the escrow

    Example: Escrow Test
    descriptionstringRequired

    Text describing the function of the escrow

    Example: Escrow Test description
    rolesobject[]Required

    Roles that make up the escrow structure

    platformFeenumberRequired

    Commission that the platform will receive when the escrow is completed

    Example: 5
    milestonesobject[]Required

    Objectives to be completed to define the escrow as completed. (In this case it is not necessary to send the properties “approvedFlag” and “status” inside the objects of these milestones)

    trustlineobject[]Required

    Information on the trustline that will manage the movement of funds in escrow

    Responses
    201

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Escrow already initialized
    • The platform fee cannot exceed 99%
    • Escrow initialized without milestone
    • Cannot define more than 50 milestones in an escrow
    • Amount cannot be zero
    • All flags (approved, disputed, released) must be false in order to execute this function
    • An unexpected error occurred
    application/json
    post
    /deployer/multi-release
    put
    Authorizations
    Body
    signerstringRequired

    Entity that signs the transaction that deploys and initializes the escrow

    Example: GSIGN...XYZ
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    escrowall ofRequired

    Escrow data to update

    Responses
    200

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • The platform fee cannot exceed 99%
    • Escrow initialized without milestone
    • Cannot define more than 50 milestones in an escrow
    • Amount cannot be zero
    • Escrow not found
    • Only the platform address should be able to execute this function
    • The platform address of the escrow cannot be changed
    • The provided escrow properties do not match the stored escrow
    • All flags (approved, disputed, released) must be false in order to execute this function
    application/json
    put
    /escrow/multi-release/update-escrow
    put
    Authorizations
    Body
    signerstringRequired

    Entity that signs the transaction that deploys and initializes the escrow

    Example: GSIGN...XYZ
    contractIdstringRequired

    ID (address) that identifies the escrow contract

    Example: CAZ6UQX7...
    escrowall ofRequired

    Escrow data to update

    Responses
    200

    This endpoint returns an unsigned transaction in XDR format. This XDR is then used to sign the transaction using the “/helper/send-transaction” endpoint.

    application/json
    400

    Bad request

    401

    Unauthorized access

    429

    Too Many Requests

    500

    Possible errors:

    • Escrow not found
    • The platform fee cannot exceed 99%
    • Amount cannot be equal to or less than zero
    • Escrow initialized without milestone
    • Cannot define more than 50 milestones in an escrow
    • Only the platform address should be able to execute this function
    • The platform address of the escrow cannot be changed
    • Escrow has been opened for dispute resolution
    • All flags (approved, disputed, released) must be false in order to execute this function
    • The provided escrow properties do not match the stored escrow
    • You can't change the escrow properties after the milestone is approved
    • An unexpected error occurred
    application/json
    put
    /escrow/single-release/update-escrow
    GET /helper/get-multiple-escrow-balance?addresses=undefined HTTP/1.1
    Host: 
    Accept: */*
    
    [
      {
        "address": "GAWVVSA..",
        "balance": 30
      },
      {
        "address": "GAWVCG3..",
        "balance": 10
      }
    ]
    GET /helper/get-escrow-by-contract-ids?contractIds=[CB25FW...%2CCBHEQBV...] HTTP/1.1
    Host: 
    Accept: */*
    
    [
      {
        "contractId": "CB25FW....",
        "signer": "GBPUA....",
        "type": "multi-release",
        "engagementId": "ENG-003",
        "title": "Title of the Escrow",
        "description": "Description of the Escrow",
        "milestones": [
          {
            "description": "Initial payment",
            "amount": 2,
            "status": "pending",
            "flags": {
              "disputed": false,
              "released": false,
              "resolved": false,
              "approved": false
            }
          },
          {
            "description": "Final payment",
            "amount": 5,
            "status": "pending",
            "flags": {
              "disputed": false,
              "released": false,
              "resolved": false,
              "approved": false
            }
          }
        ],
        "platformFee": 5,
        "receiverMemo": 0,
        "roles": {
          "approver": "GBPUACN....",
          "serviceProvider": "GA2RRI....",
          "platformAddress": "GBPA2LO....",
          "releaseSigner": "GCPZUO....",
          "disputeResolver": "GDBMRV...",
          "receiver": "GA2RRI...",
          "issuer": "GBPUAC..."
        },
        "trustline": {
          "address": "CBIELT...",
          "name": "USDC"
        },
        "isActive": true,
        "updatedAt": {
          "_seconds": 1750698602,
          "_nanoseconds": 356000000
        },
        "createdAt": {
          "_seconds": 1750698602,
          "_nanoseconds": 356000000
        },
        "balance": 0
      },
      {
        "contractId": "CF35XW....",
        "signer": "GBPUA....",
        "type": "single-release",
        "engagementId": "ENG-003",
        "title": "Title of the Escrow",
        "description": "Description of the Escrow",
        "milestones": [
          {
            "description": "Initial payment",
            "amount": 5,
            "status": "pending",
            "flags": {
              "disputed": false,
              "released": false,
              "resolved": false,
              "approved": false
            }
          },
          {
            "description": "Final payment",
            "amount": 3,
            "status": "pending",
            "flags": {
              "disputed": false,
              "released": false,
              "resolved": false,
              "approved": false
            }
          }
        ],
        "platformFee": 3,
        "receiverMemo": 0,
        "roles": {
          "approver": "GBPUACN....",
          "serviceProvider": "GA2RRI....",
          "platformAddress": "GBPA2LO....",
          "releaseSigner": "GCPZUO....",
          "disputeResolver": "GDBMRV...",
          "receiver": "GA2RRI...",
          "issuer": "GBPUAC..."
        },
        "trustline": {
          "address": "CBIELT...",
          "name": "USDC"
        },
        "isActive": true,
        "updatedAt": {
          "_seconds": 1750698602,
          "_nanoseconds": 356000000
        },
        "createdAt": {
          "_seconds": 1750698602,
          "_nanoseconds": 356000000
        },
        "balance": 3
      }
    ]
    POST /escrow/multi-release/dispute-milestone HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 83
    
    {
      "contractId": "CAZ6UQX7...",
      "milestoneIndex": "1",
      "signer": "GAPPROVER1234567890..."
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /escrow/multi-release/approve-milestone HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 85
    
    {
      "contractId": "CAZ6UQX7...",
      "milestoneIndex": "1",
      "approver": "GAPPROVER1234567890..."
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /escrow/multi-release/resolve-milestone-dispute HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 210
    
    {
      "contractId": "CAZ6UQX7...",
      "disputeResolver": "GAPPROVER1234567890...",
      "milestoneIndex": "1",
      "distributions": [
        {
          "address": "GAPPROVER1234567890...",
          "amount": 300
        },
        {
          "address": "GRECEIVER1234567890...",
          "amount": 700
        }
      ]
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /escrow/single-release/fund-escrow HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 76
    
    {
      "contractId": "CAZ6UQX7...",
      "signer": "GAPPROVER1234567890...",
      "amount": "10"
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    PUT /indexer/update-from-txhash HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 31
    
    {
      "txHash": "b0e61d4...f1cb2d29"
    }
    {
      "status": "SUCCESS",
      "message": "Escrow saved successfully / The escrow has been correctly indexed in our database.",
      "savedEscrow": {
        "contractId": "CB25FW....",
        "signer": "GBPUA....",
        "type": "multi-release",
        "engagementId": "ENG-003",
        "title": "Title of the Escrow",
        "description": "Description of the Escrow",
        "milestones": [
          {
            "description": "Initial payment",
            "amount": 2,
            "status": "pending",
            "flags": {
              "disputed": false,
              "released": false,
              "resolved": false,
              "approved": false
            }
          },
          {
            "description": "Final payment",
            "amount": 5,
            "status": "pending",
            "flags": {
              "disputed": false,
              "released": false,
              "resolved": false,
              "approved": false
            }
          }
        ],
        "platformFee": 5,
        "receiverMemo": 0,
        "roles": {
          "approver": "GBPUACN....",
          "serviceProvider": "GA2RRI....",
          "platformAddress": "GBPA2LO....",
          "releaseSigner": "GCPZUO....",
          "disputeResolver": "GDBMRV...",
          "receiver": "GA2RRI...",
          "issuer": "GBPUAC..."
        },
        "trustline": {
          "address": "CBIELT...",
          "name": "USDC"
        },
        "isActive": true,
        "updatedAt": {
          "_seconds": 1750698602,
          "_nanoseconds": 356000000
        },
        "createdAt": {
          "_seconds": 1750698602,
          "_nanoseconds": 356000000
        },
        "balance": 0
      }
    }
    POST /escrow/multi-release/change-milestone-status HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 177
    
    {
      "contractId": "CAZ6UQX7...",
      "milestoneIndex": "1",
      "newEvidence": "Any evidence that the milestone is completed",
      "newStatus": "Completed",
      "serviceProvider": "GAPPROVER1234567890..."
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /escrow/single-release/change-milestone-status HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 177
    
    {
      "contractId": "CAZ6UQX7...",
      "milestoneIndex": "1",
      "newEvidence": "Any evidence that the milestone is completed",
      "newStatus": "Completed",
      "serviceProvider": "GAPPROVER1234567890..."
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /helper/send-transaction HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 30
    
    {
      "signedXdr": "AAAAAgAAAAB..."
    }
    {
      "status": "SUCCESS",
      "message": "The transaction has been successfully sent to the Stellar network.",
      "contractId": "CAATN5D...",
      "escrow": {
        "amount": 50,
        "roles": {
          "approver": "GAWVVSA...",
          "serviceProvider": "GAWVVSA6...",
          "disputeResolver": "GAWVVSA...",
          "receiver": "GAWVVSA...",
          "platformAddress": "GAWVVSA...",
          "releaseSigner": "GAWVVSA..."
        },
        "flags": {
          "disputed": false,
          "released": false,
          "resolved": false
        },
        "description": "This is a sample TW escrow for testing purposes",
        "engagementId": "ENG12345",
        "milestones": [
          {
            "approved": false,
            "description": "Initial milestone",
            "evidence": "",
            "status": "pending"
          },
          {
            "approved": false,
            "description": "Second milestone",
            "evidence": "",
            "status": "pending"
          }
        ],
        "platformFee": 5,
        "title": "Sample TW Escrow",
        "trustline": {
          "address": "CBIELTK...",
          "name": "USDC"
        },
        "receiverMemo": 90909090
      }
    }
    POST /escrow/single-release/release-funds HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 69
    
    {
      "contractId": "CAZ6UQX7...",
      "releaseSigner": "GAPPROVER1234567890..."
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /escrow/multi-release/fund-escrow HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 76
    
    {
      "contractId": "CAZ6UQX7...",
      "signer": "GAPPROVER1234567890...",
      "amount": "10"
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /escrow/single-release/resolve-dispute HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 188
    
    {
      "contractId": "CAZ6UQX7...",
      "disputeResolver": "GAPPROVER1234567890...",
      "distributions": [
        {
          "address": "GAPPROVER1234567890...",
          "amount": 20
        },
        {
          "address": "GRECIPIENT1234567890...",
          "amount": 30
        }
      ]
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /escrow/multi-release/withdraw-remaining-funds HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 188
    
    {
      "contractId": "CAZ6UQX7...",
      "disputeResolver": "GDISPUTE1234567890...",
      "distributions": [
        {
          "address": "GAPPROVER1234567890...",
          "amount": 150
        },
        {
          "address": "GRECEIVER1234567890...",
          "amount": 850
        }
      ]
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /escrow/multi-release/release-milestone-funds HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 91
    
    {
      "contractId": "CAZ6UQX7...",
      "releaseSigner": "GAPPPROVER1234567890...",
      "milestoneIndex": "1"
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /escrow/single-release/dispute-escrow HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 62
    
    {
      "contractId": "CAZ6UQX7...",
      "signer": "GAPPROVER1234567890..."
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /escrow/single-release/approve-milestone HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 85
    
    {
      "contractId": "CAZ6UQX7...",
      "milestoneIndex": "1",
      "approver": "GAPPROVER1234567890..."
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /deployer/single-release HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 603
    
    {
      "signer": "GAPPROVER1234567890...",
      "engagementId": "ENG12345",
      "title": "Project Title",
      "description": "This is a detailed description of the project.",
      "roles": {
        "approver": "GAPPROVER1234567890...",
        "serviceProvider": "GAPPROVER1234567890...",
        "platformAddress": "GAPPROVER1234567890...",
        "releaseSigner": "GAPPROVER1234567890...",
        "disputeResolver": "GAPPROVER1234567890...",
        "receiver": "GAPPROVER1234567890..."
      },
      "amount": 1000,
      "platformFee": 50,
      "milestones": [
        {
          "description": "Initial phase of the project"
        },
        {
          "description": "Completion of design work"
        }
      ],
      "trusline": {
        "symbol": "USDC",
        "address": "GBBD47IF6LWK7P7MDEVSC..."
      }
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    POST /deployer/multi-release HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 648
    
    {
      "signer": "GAPPROVER1234567890...",
      "engagementId": "ENG12345",
      "title": "Project Title",
      "description": "This is a detailed description of the project.",
      "roles": {
        "approver": "GAPPROVER1234567890...",
        "serviceProvider": "GAPPROVER1234567890...",
        "platformAddress": "GAPPROVER1234567890...",
        "releaseSigner": "GAPPROVER1234567890...",
        "disputeResolver": "GAPPROVER1234567890..."
      },
      "platformFee": 50,
      "milestones": [
        {
          "description": "Initial phase of the project",
          "amount": 5,
          "receiver": "GAPPROVER1234567890..."
        },
        {
          "description": "Completion of design work",
          "amount": 5,
          "receiver": "GAPPROVER1234567890..."
        }
      ],
      "trustline": {
        "symbol": "USDC",
        "address": "GBBD47IF6LWK7P7MDEVSC..."
      }
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    PUT /escrow/multi-release/update-escrow HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 878
    
    {
      "signer": "GAPPROVER1234567890...",
      "contractId": "CAZ6UQX7...",
      "isActive": true,
      "escrow": {
        "engagementId": "ENG12345",
        "title": "Project Title",
        "description": "This is a detailed description of the project.",
        "roles": {
          "approver": "GAPPROVER1234567890...",
          "serviceProvider": "GAPPROVER1234567890...",
          "platformAddress": "GAPPROVER1234567890...",
          "releaseSigner": "GAPPROVER1234567890...",
          "disputeResolver": "GAPPROVER1234567890..."
        },
        "platformFee": 1,
        "milestones": [
          {
            "description": "test1",
            "status": "pending",
            "evidence": "Any evidence that the milestone is completed",
            "amount": 1000,
            "receiver": "GAPPROVER1234567890..."
          },
          {
            "description": "test2",
            "status": "pending",
            "evidence": "Any evidence that the milestone is completed",
            "amount": 1000,
            "receiver": "GAPPROVER1234567890..."
          }
        ],
        "flags": {
          "disputed": false,
          "released": false,
          "resolved": false,
          "approved": false
        },
        "trustline": {
          "address": "GAPPROVER1234567890..."
        }
      }
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    PUT /escrow/single-release/update-escrow HTTP/1.1
    Host: 
    Content-Type: application/json
    Accept: */*
    Content-Length: 846
    
    {
      "signer": "GAPPROVER1234567890...",
      "contractId": "CAZ6UQX7...",
      "isActive": true,
      "escrow": {
        "signer": "GAPPROVER1234567890...",
        "engagementId": "ENG12345",
        "title": "Project Title",
        "description": "This is a detailed description of the project.",
        "roles": {
          "approver": "GAPPROVER1234567890...",
          "serviceProvider": "GAPPROVER1234567890...",
          "platformAddress": "GAPPROVER1234567890...",
          "releaseSigner": "GAPPROVER1234567890...",
          "disputeResolver": "GAPPROVER1234567890...",
          "receiver": "GAPPROVER1234567890..."
        },
        "amount": 1000,
        "platformFee": 50,
        "milestones": [
          {
            "description": "test1",
            "status": "pending",
            "evidence": "Any evidence that the milestone is completed"
          },
          {
            "description": "test2",
            "status": "pending",
            "evidence": "Any evidence that the milestone is completed"
          }
        ],
        "flags": {
          "disputed": false,
          "released": false,
          "resolved": false
        },
        "trustline": {
          "address": "GAPPROVER1234567890..."
        }
      }
    }
    {
      "status": "SUCCESS",
      "unsignedTransaction": "AAAAAgAAAAAtWsgedQ...."
    }
    [
      {
        "contractId": "CB25FW....",
        "signer": "GBPUA....",
        "type": "multi-release",
        "engagementId": "ENG-003",
        "title": "Title of the Escrow",
        "description": "Description of the Escrow",
        "milestones": [
          {
            "description": "Initial payment",
            "amount": 2,
            "status": "pending",
            "flags": {
              "disputed": false,
              "released": false,
              "resolved": false,
              "approved": false
            }
          },
          {
            "description": "Final payment",
            "amount": 5,
            "status": "pending",
            "flags": {
              "disputed": false,
              "released": false,
              "resolved": false,
              "approved": false
            }
          }
        ],
        "platformFee": 5,
        "receiverMemo": 0,
        "roles": {
          "approver": "GBPUACN....",
          "serviceProvider": "GA2RRI....",
          "platformAddress": "GBPA2LO....",
          "releaseSigner": "GCPZUO....",
          "disputeResolver": "GDBMRV...",
          "receiver": "GA2RRI...",
          "issuer": "GBPUAC..."
        },
        "trustline": {
          "address": "CBIELT...",
          "name": "USDC"
        },
        "isActive": true,
        "updatedAt": {
          "_seconds": 1750698602,
          "_nanoseconds": 356000000
        },
        "createdAt": {
          "_seconds": 1750698602,
          "_nanoseconds": 356000000
        },
        "balance": 0
      }
    ]
    GET /helper/get-escrows-by-role HTTP/1.1
    Host: 
    Accept: */*
    
    [
      {
        "contractId": "CB25FW....",
        "signer": "GBPUA....",
        "type": "multi-release",
        "engagementId": "ENG-003",
        "title": "Title of the Escrow",
        "description": "Description of the Escrow",
        "milestones": [
          {
            "description": "Initial payment",
            "amount": 2,
            "status": "pending",
            "flags": {
              "disputed": false,
              "released": false,
              "resolved": false,
              "approved": false
            }
          },
          {
            "description": "Final payment",
            "amount": 5,
            "status": "pending",
            "flags": {
              "disputed": false,
              "released": false,
              "resolved": false,
              "approved": false
            }
          }
        ],
        "platformFee": 5,
        "receiverMemo": 0,
        "roles": {
          "approver": "GBPUACN....",
          "serviceProvider": "GA2RRI....",
          "platformAddress": "GBPA2LO....",
          "releaseSigner": "GCPZUO....",
          "disputeResolver": "GDBMRV...",
          "receiver": "GA2RRI...",
          "issuer": "GBPUAC..."
        },
        "trustline": {
          "address": "CBIELT...",
          "name": "USDC"
        },
        "isActive": true,
        "updatedAt": {
          "_seconds": 1750698602,
          "_nanoseconds": 356000000
        },
        "createdAt": {
          "_seconds": 1750698602,
          "_nanoseconds": 356000000
        },
        "balance": 0
      }
    ]
    GET /helper/get-escrows-by-signer HTTP/1.1
    Host: 
    Accept: */*