Version: 1.0.0
License: MIT
Once the server is running, access the Swagger UI at:
http://localhost:3001/api/docs
The raw OpenAPI specification is available at:
http://localhost:3001/api/docs-json
The TrustFlow Backend API provides off-chain services for the TrustFlow gig economy platform. It handles:
- Authentication: Wallet-based JWT authentication using Stellar signatures
- Escrow Management: Create, manage, and release escrow vaults
- Dispute Resolution: Raise disputes and trigger juror notifications
- Webhooks: Register endpoints to receive event notifications
- Monitoring: Health checks and Prometheus metrics
- IPFS Pinning: Pin deliverables across multiple IPFS providers with content-hash verification, automatic failover, and a background re-pin worker for durability
-
Get Challenge:
GET /auth/challenge?address=YOUR_ADDRESS- Receive a challenge message to sign
-
Sign with Wallet: Sign the challenge using your Stellar wallet
-
Verify Signature:
POST /auth/verify{ "address": "GXXXXX...", "signature": "base64_signature..." } -
Receive JWT Token: Use this token in the
AuthorizationheaderAuthorization: Bearer YOUR_JWT_TOKEN
All non-monitoring endpoints are protected by a Redis-backed distributed token bucket so limits remain coordinated across multiple API nodes.
- Per-IP limits: Every request consumes from a route-specific bucket keyed by client IP.
- Per-wallet limits: Requests that include wallet identity consume a second route-specific bucket keyed by wallet address. Wallet identity is read from JWT user data, request body, query string, or route params.
- Abuse detection: Empty-bucket attempts are tracked in a Redis sorted set over a sliding abuse window.
- Lockouts: Repeated violations create temporary Redis lockout keys and return
429 Too Many Requestswithout consuming more bucket state.
{
"statusCode": 429,
"message": "Too many requests - rate limit exceeded",
"retryAfter": 30,
"scope": "wallet:gabc123"
}REDIS_URL=redis://localhost:6379
RATE_LIMIT_ABUSE_WINDOW_SECONDS=300
RATE_LIMIT_ABUSE_THRESHOLD=5
RATE_LIMIT_LOCKOUT_SECONDS=900/health and /metrics are exempt through @SkipRateLimit().
- Get your challenge and sign it
- Verify and receive a JWT token
- Click the π "Authorize" button in Swagger UI
- Enter your token (without "Bearer" prefix)
- All protected endpoints will now include your auth token
| Method | Endpoint | Description |
|---|---|---|
| GET | /auth/challenge |
Get authentication challenge |
| POST | /auth/verify |
Verify wallet signature and get JWT |
| Method | Endpoint | Description |
|---|---|---|
| POST | /escrows |
Create new escrow |
| GET | /escrows/:id |
Get escrow by ID |
| GET | /escrows/depositor/:address |
Get escrows by depositor |
| POST | /escrows/:id/release |
Release escrow funds |
| POST | /escrows/:id/dispute |
Raise a dispute |
| Method | Endpoint | Description |
|---|---|---|
| POST | /webhooks |
Register webhook |
| DELETE | /webhooks/:id |
Unregister webhook |
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Health check |
| GET | /metrics |
Prometheus metrics |
| Method | Endpoint | Description |
|---|---|---|
| POST | /ipfs/pins |
Pin content across multiple providers with content-hash verification |
| GET | /ipfs/pins |
List all pin records |
| GET | /ipfs/pins/:cid |
Get a pin record by CID |
| POST | /ipfs/pins/:cid/verify |
Re-verify durability and top up replication if degraded |
| DELETE | /ipfs/pins/:cid |
Unpin from every provider currently holding the content |
curl -X POST http://localhost:3001/escrows \
-H "Content-Type: application/json" \
-d '{
"depositor": "GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"beneficiary": "GYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
"amountXLM": "100"
}'Response:
{
"id": "esc-1234567890",
"depositor": "GXXXXX...",
"beneficiary": "GYYYY...",
"amountXLM": "100",
"status": "pending",
"createdAt": "2026-06-13T00:00:00.000Z"
}curl -X POST http://localhost:3001/escrows/esc-1234567890/dispute \
-H "Content-Type: application/json" \
-d '{
"reason": "Work not delivered as specified"
}'Response:
{
"id": "esc-1234567890",
"status": "disputed",
"disputeReason": "Work not delivered as specified",
"disputedAt": "2026-06-13T01:00:00.000Z"
}Note: This also triggers:
- Webhook event (
dispute.raised) - Discord notification (if configured)
curl -X POST http://localhost:3001/webhooks \
-H "Content-Type: application/json" \
-d '{
"id": "my-webhook",
"url": "https://example.com/webhooks/trustflow"
}'Response:
{
"registered": true,
"id": "my-webhook"
}curl -X POST http://localhost:3001/ipfs/pins \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"content": "SGVsbG8sIFRydXN0RmxvdyE=",
"filename": "milestone-1-receipt.json",
"replicationFactor": 2
}'Response:
{
"cid": "bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku",
"size": 20,
"filename": "milestone-1-receipt.json",
"replicationFactor": 2,
"status": "HEALTHY",
"providers": [
{ "provider": "pinata", "status": "PINNED", "attempts": 1, "pinnedAt": "..." },
{ "provider": "web3.storage", "status": "PINNED", "attempts": 1, "pinnedAt": "..." }
],
"createdAt": "...",
"updatedAt": "..."
}The cid is derived purely from the submitted bytes (CIDv1, raw, sha2-256), so any provider that
ends up storing different bytes fails content-hash verification and is automatically failed over.
If a provider later loses the pin, POST /ipfs/pins/:cid/verify (also run automatically by the
background re-pin worker) detects it and restores replication via a spare provider.
When you register a webhook, you'll receive POST requests for these events:
| Event | Description | Payload |
|---|---|---|
escrow.created |
New escrow created | Escrow details |
escrow.released |
Funds released | Escrow details |
dispute.raised |
Dispute initiated | Dispute details |
dispute.resolved |
Dispute resolved | Resolution details |
ipfs.pin.created |
Content newly pinned | CID, replication factor, pinned providers |
ipfs.pin.degraded |
Pin dropped below its replication factor | CID |
ipfs.pin.restored |
Replication restored after a loss | CID, healthy provider count |
ipfs.pin.lost |
A provider no longer holds a previously-pinned CID | CID, provider |
ipfs.pin.failed |
Every registered provider failed to pin a CID | CID |
ipfs.pin.removed |
Content unpinned from all providers | CID |
{
"event": "dispute.raised",
"data": {
"escrowId": "esc-1234567890",
"depositor": "GXXXXX...",
"beneficiary": "GYYYY...",
"amountXLM": "100",
"reason": "Work not delivered",
"disputedAt": "2026-06-13T01:00:00.000Z"
},
"timestamp": "2026-06-13T01:00:00.000Z"
}| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request - Invalid input |
| 401 | Unauthorized - Invalid or missing JWT |
| 404 | Not Found - Resource doesn't exist |
| 500 | Internal Server Error |
| 503 | Service Unavailable |
cd backend
npm install
npm run devThe API will be available at: http://localhost:3001
Swagger UI will be at: http://localhost:3001/api/docs
PORT=3001
JWT_SECRET=your-secret
REDIS_URL=redis://localhost:6379
RATE_LIMIT_ABUSE_WINDOW_SECONDS=300
RATE_LIMIT_ABUSE_THRESHOLD=5
RATE_LIMIT_LOCKOUT_SECONDS=900
STELLAR_NETWORK=TESTNET
STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org
SOROBAN_RPC_URL=https://soroban-testnet.stellar.org
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/... (optional)
# IPFS pinning providers (all optional β unconfigured providers run in an
# in-memory simulated mode so pinning/failover work out of the box in dev/CI)
PINATA_JWT=
WEB3_STORAGE_TOKEN=
INFURA_IPFS_PROJECT_ID=
INFURA_IPFS_PROJECT_SECRET=
IPFS_REPIN_INTERVAL_MS=300000# Get the specification
curl http://localhost:3001/api/docs-json > openapi.jsonThe OpenAPI specification can be used with:
- Postman: Import the JSON to create a collection
- Insomnia: Import for API testing
- Code Generators: Generate client SDKs
# Generate TypeScript client npx @openapitools/openapi-generator-cli generate \ -i http://localhost:3001/api/docs-json \ -g typescript-axios \ -o ./generated-client
- Start the server:
npm run dev - Open Swagger UI: http://localhost:3001/api/docs
- Try an endpoint:
- Click on any endpoint (e.g.,
GET /health) - Click "Try it out"
- Fill in parameters (if any)
- Click "Execute"
- View the response
- Click on any endpoint (e.g.,
-
Get Challenge:
- Expand
GET /auth/challenge - Enter your Stellar address
- Execute
- Copy the challenge message
- Expand
-
Sign with your wallet (outside Swagger)
-
Verify Signature:
- Expand
POST /auth/verify - Enter address and signature
- Execute
- Copy the JWT token
- Expand
-
Authorize:
- Click π "Authorize" button at top
- Paste your JWT token
- Click "Authorize"
-
Test Protected Endpoints: Now you can test escrow endpoints!
When adding new endpoints, include Swagger decorators:
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
@ApiTags('YourTag')
@Controller('your-route')
export class YourController {
@Get()
@ApiOperation({ summary: 'Your endpoint summary' })
@ApiResponse({ status: 200, description: 'Success response' })
yourMethod() {
// ...
}
}@ApiTags()- Group endpoints by tag@ApiOperation()- Describe the endpoint@ApiResponse()- Document response schemas@ApiParam()- Document path parameters@ApiQuery()- Document query parameters@ApiBody()- Document request body@ApiBearerAuth()- Mark as requiring JWT
When adding new API endpoints:
- β Add Swagger decorators to controllers
- β Document all parameters and responses
- β Test in Swagger UI
- β Update this documentation if needed
- Documentation: http://localhost:3001/api/docs
- Issues: https://github.com/trustflow-protocol/trustflow-backend/issues
- Community: Discord (link in main README)
Auto-generated API documentation powered by Swagger/OpenAPI