FlowPilotAPI Referencev1
v1.0 · REST API
FlowPilot

Build integrations against the FlowPilot treasury execution platform. Manage runs, recipients, wallet balance, transactions, and audit events programmatically.

Base URLhttps://api.flowpilot.club/api/v1
Go to Dashboard

Overview

The FlowPilot Public API lets you automate treasury operations — from creating payment runs to approving candidates and querying transaction history. All endpoints return JSON and follow standard REST conventions.

API Keys

Authenticate with scoped API keys generated in the FlowPilot dashboard.

Paginated lists

Every list endpoint returns a unified envelope with total, limit, offset, and has_more.

Scoped access

Each operation requires a specific scope, ensuring least-privilege access control.

Authentication

All API requests must include a valid API key. Keys are prefixed with fp_live_ for production and fp_test_ for test mode. Generate keys in the FlowPilot dashboard under Settings → Developer.

Passing your key

http
# Option 1 — dedicated header
X-API-Key: fp_live_xxxxxxxxxxxxxxxxxxxx

# Option 2 — Bearer token
Authorization: Bearer fp_live_xxxxxxxxxxxxxxxxxxxx

Available scopes

ScopeDescription
runs:readRead runs and their metadata
runs:writeCreate new payment runs
recipients:readList saved recipients
recipients:writeCreate or delete saved recipients
wallet:readRead wallet balance and AI credit balance
transactions:readQuery transaction history
audit:readAccess the audit event log

Pagination

All list endpoints accept limit and offset query parameters. The maximum value for limit is 200. Defaults to limit=50&offset=0.

json
{
  "data": [...],
  "total": 247,
  "limit": 50,
  "offset": 0,
  "has_more": true
}

Use has_more: true to determine whether additional pages exist. Increment offset by limit to fetch the next page.

Errors

FlowPilot uses standard HTTP status codes. All error responses return a JSON object with a human-readable detail field.

json
{
  "detail": "Invalid or expired API key."
}
StatusMeaning
401UnauthorizedMissing or invalid API key.
403ForbiddenValid key but insufficient scope for this operation.
404Not FoundThe requested resource does not exist.
422UnprocessableRequest body failed validation — check the detail field.
429Rate LimitedToo many requests. Retry after the Retry-After header value.

Runs

A Runrepresents a single payment batch processed by FlowPilot's AI agents — from candidate scoring to disbursement. Runs move through a defined lifecycle: pending → planning → scoring → awaiting_approval → completed | failed.

GET/public/runs

Return a paginated list of all payment runs for your organisation. Filter by status to narrow results.

Query Parameters

ParameterTypeRequiredDescription
limitintegerNoNumber of records to return (default 50, max 200)
offsetintegerNoNumber of records to skip for pagination
statusstringNoFilter by status: pending | planning | scoring | awaiting_approval | completed | failed

Response

json
{
  "data": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "objective": "Monthly payroll disbursement for Lagos office — June 2026",
      "status": "completed",
      "risk_tolerance": 0.35,
      "budget_cap": 50000000.00,
      "candidate_count": 142,
      "created_at": "2026-06-01T08:00:00Z",
      "updated_at": "2026-06-01T08:47:23Z"
    }
  ],
  "total": 24,
  "limit": 50,
  "offset": 0,
  "has_more": false
}
GET/public/runs/{run_id}

Fetch a single run by its UUID. Returns the same shape as the list endpoint item.

Response

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "objective": "Monthly payroll disbursement for Lagos office — June 2026",
  "status": "completed",
  "risk_tolerance": 0.35,
  "budget_cap": 50000000.00,
  "candidate_count": 142,
  "created_at": "2026-06-01T08:00:00Z",
  "updated_at": "2026-06-01T08:47:23Z"
}
POST/public/runs201runs:write

Create a new payment run. The AI planning and scoring agents are triggered automatically after creation.

Request Body

json
{
  "objective": "Vendor payment batch — Q2 suppliers",
  "risk_tolerance": 0.30,
  "budget_cap": 10000000.00
}

Response

json
{
  "id": "7b8a9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d",
  "status": "pending",
  "objective": "Vendor payment batch — Q2 suppliers",
  "created_at": "2026-06-15T10:30:00Z"
}

Candidates

Candidates are the individual payment records within a run. Each candidate has a risk score, risk decision, and approval status. Candidates can be approved or rejected via the Approvals endpoints.

GET/public/runs/{run_id}/candidates

Return a paginated list of candidates belonging to the specified run. Filter by approval status to see only pending, approved, or rejected records.

Query Parameters

ParameterTypeRequiredDescription
limitintegerNoNumber of records to return (default 50, max 200)
offsetintegerNoNumber of records to skip for pagination
approval_statusstringNoFilter by approval_status: pending | approved | rejected

Response

json
{
  "data": [
    {
      "id": "a1b2c3d4-e5f6-7a8b-9c0d-e1f2a3b4c5d6",
      "run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "beneficiary_name": "Adaeze Okonkwo",
      "account_number": "0123456789",
      "institution_code": "058",
      "amount": 350000.00,
      "currency": "NGN",
      "risk_score": 0.12,
      "risk_decision": "auto_approved",
      "approval_status": "approved",
      "execution_status": "success"
    }
  ],
  "total": 142,
  "limit": 50,
  "offset": 0,
  "has_more": true
}

Recipients

Saved recipients let you store frequently paid beneficiaries once and reuse them across multiple payout runs. Each recipient has a name, account number, bank code, and an optional default narration note.

GET/public/recipientsrecipients:read

Return a paginated list of saved recipients for your organisation. Use the search parameter to filter by name or account number.

Query Parameters

ParameterTypeRequiredDescription
searchstringNoFilter by name or account number (partial match)
limitintegerNoNumber of records to return (default 50, max 200)
offsetintegerNoNumber of records to skip for pagination

Response

json
{
  "data": [
    {
      "id": "d1e2f3a4-b5c6-7d8e-9f0a-1b2c3d4e5f6a",
      "name": "Adaeze Okonkwo",
      "account_number": "0123456789",
      "institution_code": "058",
      "bank_name": "GTBank",
      "narration_note": "Monthly consulting fee",
      "created_at": "2026-03-01T10:00:00Z"
    }
  ],
  "total": 18,
  "limit": 50,
  "offset": 0,
  "has_more": false
}
POST/public/recipients201recipients:write

Create a new saved recipient. The institution_code is the CBN-assigned bank code (e.g. 058 for GTBank).

Request Body

json
{
  "name": "Adaeze Okonkwo",
  "account_number": "0123456789",
  "institution_code": "058",
  "bank_name": "GTBank",
  "narration_note": "Monthly consulting fee"
}

Response

json
{
  "id": "d1e2f3a4-b5c6-7d8e-9f0a-1b2c3d4e5f6a",
  "name": "Adaeze Okonkwo",
  "account_number": "0123456789",
  "institution_code": "058",
  "bank_name": "GTBank",
  "narration_note": "Monthly consulting fee",
  "created_at": "2026-04-17T10:00:00Z"
}
DELETE/public/recipients/{recipient_id}recipients:write

Permanently delete a saved recipient. This does not affect past payout runs that referenced the recipient.

Response

json
{
  "status": "deleted",
  "id": "d1e2f3a4-b5c6-7d8e-9f0a-1b2c3d4e5f6a"
}

Wallet

Your FlowPilot wallet is debited on each approved payout run. Fund it via bank transfer to your virtual account in the dashboard. The wallet endpoint also returns your remaining AI credit balance.

GET/public/wallet/balancewallet:read

Return the current wallet balance (NGN), currency, and remaining AI credit balance for your organisation.

Response

json
{
  "balance": 12500000.00,
  "currency": "NGN",
  "ai_credit_balance": 47,
  "updated_at": "2026-04-17T09:45:00Z"
}

Transactions

Transactions represent the actual fund movements generated by approved candidates. Each transaction has a unique reference, amount, direction, and settlement status. Requires the transactions:read scope.

GET/public/transactions

Return a paginated list of all transactions. Filter by run_id to retrieve transactions for a specific payment batch, or by status to view only successful, pending, or failed transfers.

Query Parameters

ParameterTypeRequiredDescription
limitintegerNoNumber of records to return (default 50, max 200)
offsetintegerNoNumber of records to skip for pagination
run_idstring (UUID)NoFilter transactions to a specific run
statusstringNoFilter by status: SUCCESS | PENDING | FAILED

Response

json
{
  "data": [
    {
      "id": "c3d4e5f6-a7b8-9c0d-1e2f-3a4b5c6d7e8f",
      "run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "reference": "FP-20260601-00142",
      "amount": 350000.00,
      "currency": "NGN",
      "direction": "debit",
      "status": "SUCCESS",
      "channel": "NIP",
      "created_at": "2026-06-01T08:45:12Z"
    }
  ],
  "total": 142,
  "limit": 50,
  "offset": 0,
  "has_more": true
}

Audit Log

The audit log records every action taken by FlowPilot's AI agents and human operators throughout a run's lifecycle. Useful for compliance reporting and debugging. Requires the audit:read scope.

GET/public/audit

Return a paginated list of audit events. Filter by run_id to retrieve the full event history for a specific run, or by action to query a specific event type.

Query Parameters

ParameterTypeRequiredDescription
limitintegerNoNumber of records to return (default 50, max 200)
offsetintegerNoNumber of records to skip for pagination
run_idstring (UUID)NoScope results to a specific run
actionstringNoFilter by action name, e.g. scored_candidates, approved_candidate

Response

json
{
  "data": [
    {
      "id": "e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9a0b",
      "run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "agent_type": "risk",
      "action": "scored_candidates",
      "detail": {
        "candidates_scored": 142,
        "flagged": 3
      },
      "created_at": "2026-06-01T08:22:41Z"
    }
  ],
  "total": 89,
  "limit": 50,
  "offset": 0,
  "has_more": false
}

Webhooks

FlowPilot can push real-time event notifications to any HTTPS endpoint you register. Configure webhooks from the Developer → Webhooks tab in your dashboard. A signing secret (whsec_…) is generated on creation and shown exactly once — save it immediately.

Available events

EventWhen it fires
run.completedA payout run finished and all approved candidates were processed
run.failedA payout run encountered a fatal error
approval.requestedA run moved to awaiting_approval — approvers need to act
approval.completedA run was approved or rejected and execution resumed
payout.succeededAn individual payout candidate was disbursed successfully
payout.failedAn individual payout candidate failed to disburse
candidate.flaggedA candidate was flagged with a high risk score during scoring
webhook.testSent once when the webhook is first registered, to verify reachability

Payload structure

Every delivery is a JSON POST with the following envelope. The data field varies by event type.

json
{
  "event": "run.completed",
  "timestamp": "2026-04-14T18:30:00.000Z",
  "delivery_id": "d3f4a5b6-...",
  "data": {
    "run_id": "a1b2c3d4-...",
    "objective": "Pay 50 field agents for April",
    "status": "completed",
    "approved_count": 48
  }
}

Request headers

HeaderDescription
X-FlowPilot-EventThe event name, e.g. run.completed
X-FlowPilot-SignatureHMAC-SHA256 signature of the raw request body: sha256=<hex>
X-FlowPilot-DeliveryUnique UUID for this delivery attempt
Content-Typeapplication/json
User-AgentFlowPilot-Webhooks/1.0

Verifying signatures

FlowPilot signs every payload with HMAC-SHA256using the webhook's signing secret. Always verify the signature before processing a delivery.

node.js
const crypto = require("crypto");

function verifyWebhook(rawBody, signatureHeader, secret) {
  const expected = "sha256=" +
    crypto.createHmac("sha256", secret)
          .update(rawBody)   // rawBody must be the raw Buffer, not parsed JSON
          .digest("hex");

  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader);
  if (a.length !== b.length) return false;
  return crypto.timingSafeEqual(a, b);
}

// Express example
app.post("/webhooks/flowpilot", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["x-flowpilot-signature"];
  if (!verifyWebhook(req.body, sig, process.env.FLOWPILOT_WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }
  const event = JSON.parse(req.body);
  console.log("Received:", event.event, event.data);
  res.sendStatus(200);
});
python
import hashlib, hmac

def verify_webhook(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

# FastAPI example
@app.post("/webhooks/flowpilot")
async def handle_webhook(request: Request):
    raw_body = await request.body()
    sig = request.headers.get("x-flowpilot-signature", "")
    if not verify_webhook(raw_body, sig, FLOWPILOT_WEBHOOK_SECRET):
        raise HTTPException(status_code=401, detail="Invalid signature")
    event = await request.json()
    return {"ok": True}

Important

  • Always read the raw request body bytes for signature verification — do not parse JSON first.
  • Your endpoint must return a 2xx status within 10 seconds or the delivery is counted as a failure.
  • After 5 consecutive failures FlowPilot will auto-disable the webhook. Re-enable it from the dashboard once your endpoint is healthy.