Trustless Work enables trust-minimized conditional payments on Stellar blockchain using Soroban smart contracts. It's ideal for freelancing, gig work, and milestone-based projects.
- Deploy: Initialize escrow with roles, milestones, and configuration
- Fund: Lock funds (escrow amount + platform fee) in escrow account
- Complete Milestone: Service provider marks milestone(s) as complete
- Approve: Approver verifies and approves milestone(s)
- Release: Funds released to Service Provider or Receiver (single payment)
- Dispute (optional): Any party can initiate dispute
- Resolve: Dispute Resolver decides release or refund
- Deploy: Initialize escrow with roles and milestones
- Fund: Lock total funds (sum of all milestone amounts + platform fee)
- Complete Milestone: Service provider marks milestone as complete
- Approve: Approver verifies and approves milestone
- Release: Funds for that milestone released immediately
- Repeat: Steps 3-5 for each milestone
- Withdraw Remaining: Dispute Resolver can withdraw remaining funds after completion
- Payer/Signer: Funds the escrow, signs deployment transaction
- Service Provider: Delivers deliverables, marks milestones complete
- Approver: Verifies deliverables, approves milestones for release
- Dispute Resolver: Intervenes in disagreements, decides release/refund
- Receiver: Final recipient of funds (defaults to Service Provider if not specified)
- Release Signer: Executes release/refund transactions (platform role)
interface Role {
address: string; // Stellar account address
type: 'payer' | 'serviceProvider' | 'approver' | 'disputeResolver' | 'receiver' | 'releaseSigner';
}Example:
{
"roles": [
{
"address": "GABC123...",
"type": "payer"
},
{
"address": "GDEF456...",
"type": "serviceProvider"
},
{
"address": "GGHI789...",
"type": "approver"
},
{
"address": "GJKL012...",
"type": "disputeResolver"
}
]
}Status tracked via boolean flags:
- approved: Milestone(s) approved for release
- dispute: Escrow is in dispute
- released: Funds have been released
- resolved: Dispute has been resolved
All API requests require an API key:
Authorization: Bearer YOUR_API_KEY
- Connect wallet to https://dapp.trustlesswork.com/dashboard
- Click wallet address (bottom left)
- Go to Settings
- Complete profile (name, email, use case - required)
- Generate API key
Production: https://api.trustlesswork.com
enum ApiErrorTypes {
NOT_FOUND = "NOT_FOUND",
UNAUTHORIZED = "UNAUTHORIZED",
UNKNOWN_ERROR = "UNKNOWN_ERROR",
WALLET_ERROR = "WALLET_ERROR",
}- 200/201: Success
- 400: Bad request (missing/invalid parameters)
- 401: Unauthorized (invalid/missing API key)
- 429: Too many requests (rate limiting)
- 500: Server error (escrow not found, unexpected errors)
All escrow operations follow this pattern:
- Call API endpoint → Returns unsigned XDR transaction
- Sign transaction → Use wallet to sign XDR
- Submit transaction → POST to
/helper/send-transactionwith signed XDR - Verify on-chain → Query escrow with
validateOnChain=true
// 1. Get unsigned transaction
const response = await fetch('/deployer/single-release', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(deployPayload)
});
const { xdr } = await response.json();
// 2. Sign with wallet
const signedXdr = await wallet.signTransaction(xdr);
// 3. Submit transaction
const submitResponse = await fetch('/helper/send-transaction', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ xdr: signedXdr })
});
// 4. Verify on-chain
const verifyResponse = await fetch(
`/helper/get-escrow-by-contract-ids?contractIds=${contractId}&validateOnChain=true`,
{
headers: {
'Authorization': `Bearer ${apiKey}`
}
}
);- Never expose API keys in client-side code or public repos
- Use environment variables for all sensitive configuration
- Validate on-chain when displaying escrow data (
validateOnChain=true) - Verify transaction signatures before submitting
- Handle errors gracefully with user-friendly messages
async function callTrustlessWorkAPI(endpoint: string, options: RequestInit) {
try {
const response = await fetch(`https://api.trustlesswork.com${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${process.env.TRUSTLESS_WORK_API_KEY}`,
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const error = await response.json().catch(() => ({ message: 'Unknown error' }));
switch (response.status) {
case 401:
throw new Error('Invalid API key. Check your API key in settings.');
case 404:
throw new Error('Escrow not found');
case 429:
throw new Error('Rate limit exceeded. Please try again later.');
default:
throw new Error(error.message || `API error: ${response.status}`);
}
}
return await response.json();
} catch (error) {
console.error('Trustless Work API Error:', error);
throw error;
}
}- Track escrow status locally but always verify with API
- Use
validateOnChain=truefor critical operations - Poll for status updates during active workflows
- Cache escrow data but refresh before important actions