Skip to main content
All FlowGuard products are backed by a single Express API. Frontend apps should point VITE_API_URL to this backend.

Base URL

http://localhost:3001/api
In production this points to your Railway (or equivalent) backend deployment.

Authentication

Guarded routes use a SIWX (Sign-In With X) bearer flow. Wallets sign a CAIP-122 multi-line message via BIP-322 once per session, exchange the signature for a short-lived bearer token, then attach that token to subsequent requests. The legacy x-user-address header is still accepted during migration, but the bearer token is preferred. New integrations should use the bearer flow.

Flow

1

Request a nonce

The frontend calls POST /api/auth/nonce with the wallet address. The backend returns a single-use nonce and the exact CAIP-122 message the wallet must sign.
2

Sign and verify

The wallet signs the message using BIP-322 (chipnet or mainnet). The frontend posts the signature to POST /api/auth/verify and receives a bearer token. Tokens are HMAC-SHA256 signed and expire 30 minutes after issue.
3

Authorize requests

Subsequent guarded calls send Authorization: Bearer <token>. On 401, clients should refresh by repeating steps 1 and 2.

Nonce

POST /api/auth/nonce
Content-Type: application/json
{
  "address": "bchtest:qr..."
}
Response:
{
  "nonce": "f3c1a9d8b27e4c0a",
  "message": "flowguard.app wants you to sign in with your Bitcoin Cash account:\nbchtest:qr...\n\nURI: https://flowguard.app\nVersion: 1\nChain ID: bch:chipnet\nNonce: f3c1a9d8b27e4c0a\nIssued At: 2026-06-11T12:00:00.000Z",
  "issuedAt": "2026-06-11T12:00:00.000Z"
}

Verify

POST /api/auth/verify
Content-Type: application/json
{
  "address": "bchtest:qr...",
  "message": "flowguard.app wants you to sign in with your Bitcoin Cash account:\nbchtest:qr...\n\nURI: https://flowguard.app\nVersion: 1\nChain ID: bch:chipnet\nNonce: f3c1a9d8b27e4c0a\nIssued At: 2026-06-11T12:00:00.000Z",
  "signature": "IEx9...BIP322Base64..."
}
Response:
{
  "token": "eyJhZGRyZXNzIjoiYmNodGVzdDpxci4uLiIsImV4cCI6MTc0OTY0NDgwMH0.9a1b...hmac",
  "address": "bchtest:qr...",
  "expiresAt": "2026-06-11T12:30:00.000Z"
}
FieldTypeNotes
tokenstringHMAC-SHA256 bearer, 30-minute TTL
addressstringCashAddr the token is bound to
expiresAtstring (ISO 8601)Token expiry

Authed request

GET /api/streams/mine
Authorization: Bearer <flowguard-siwx-bearer-from-verify-response>
Curl example:
curl https://api.flowguard.app/api/streams/mine \
  -H "Authorization: Bearer $FLOWGUARD_TOKEN"

Frontend SDK helper

The frontend ships authFetch in frontend/src/utils/auth.ts. It wraps fetch and handles the SIWX round-trip transparently:
  • Caches the bearer in sessionStorage keyed by wallet address.
  • Reuses the cached token until it expires.
  • On 401, drops the cached token, repeats nonce + verify, and retries the original request once.
import { authFetch } from "@/utils/auth";

const res = await authFetch("/api/streams/mine");
const streams = await res.json();
Some create/build routes still take the wallet address in the request body (for example /api/streams/create). Follow each endpoint’s request spec.

WalletConnect Transactions

Most on-chain build endpoints return a wcTransaction object rather than broadcasting directly. The frontend passes this to the connected wallet (Paytaca, Selene) via WalletConnect v2 using bch_signTransaction. After the wallet signs and broadcasts, the frontend calls the corresponding confirm-* endpoint with the resulting txHash.
{
  "wcTransaction": {
    "transaction": "0200...",
    "sourceOutputs": [...],
    "broadcast": true,
    "userPrompt": "Fund vesting stream"
  }
}

Common Response Shape

Many endpoints include "success": true at the top level:
{
  "success": true,
  "stream": { ... }
}
Some read/list endpoints return plain JSON arrays/objects without a success wrapper. Errors return a non-2xx status with "error" and optionally "message":
{
  "error": "Stream not found"
}

Lifecycle Pattern

Most on-chain actions follow this two-step lifecycle:
1

Build

Call a build endpoint that returns a wallet payload (for example GET /api/streams/:id/funding-info, POST /api/streams/:id/claim, or POST /api/airdrops/:id/pause).
2

Confirm

After wallet broadcast, call the corresponding confirm-* endpoint with txHash. The backend verifies on-chain outputs and updates database state.
Non-transactional endpoints (for example list/get metadata routes) return data directly and do not use build/confirm. Some create endpoints (/api/streams/create, /api/payments/create, /api/airdrops/create) return deployment metadata first, then require a separate funding build call.

Status Values

Most stream/distribution resources share this status lifecycle:
StatusMeaning
PENDINGContract deployed, awaiting funding
ACTIVEFunded and live on-chain
PAUSEDTemporarily halted by authority
CANCELLEDTerminated early, funds returned
COMPLETEDAll obligations fulfilled
Proposal and governance entities use their own status enums (for example pending, approved, executed, rejected for treasury proposals).