# FlowCheck API — Complete Reference > Stripe payouts, Shopify orders, and bank data — one API. Connect, reconcile, build. - Support: support@usepopup.com - Docs: https://developer.usepopup.com/llms.txt Base URL: `https://developer.usepopup.com/api/v0` --- ## Authentication All endpoints (except registration) require a Bearer token: ``` Authorization: Bearer fc_live_your_key_here ``` Key prefixes: - `fc_live_*` — production - `fc_test_*` — sandbox (free, no credits consumed) Keys are shown once at creation and stored as SHA-256 hashes. They cannot be retrieved later. --- ## Response Envelope Every response follows this structure: ```json { "data": { ... }, "meta": { "request_id": "req_abc123", "timestamp": "2026-03-05T10:00:00Z", "version": "v0", "credits_remaining": 847 }, "errors": [] } ``` List endpoints include pagination in `meta`: ```json { "meta": { "request_id": "req_abc123", "timestamp": "2026-03-05T10:00:00Z", "version": "v0", "cursor": "eyJpZCI6MTAwfQ", "has_more": true } } ``` --- ## Error Format Errors follow RFC 7807 (Problem Details for HTTP APIs): ```json { "type": "https://developer.usepopup.com/errors/invalid-api-key", "title": "Invalid API Key", "status": 401, "detail": "The API key provided is not valid or has been revoked.", "instance": "/v0/balance" } ``` Common error codes: - `400` — Bad request (missing/invalid parameters) - `401` — Invalid or missing API key - `403` — Insufficient permissions or plan - `404` — Resource not found - `429` — Rate limit exceeded (check `Retry-After` header) - `500` — Internal server error (includes request_id for support) --- ## Pagination All list endpoints use cursor-based pagination: Query parameters: - `limit` (integer, default 50, max 100) — number of items per page - `cursor` (string) — cursor from previous response Example: ``` GET /v0/payouts?limit=25&cursor=eyJpZCI6MTAwfQ ``` --- ## Rate Limits | Plan | Credits/mo | Rate Limit | |------|-----------|------------| | Trial | 100 | 60 req/min | | Starter | 1,000 | 300 req/min | | Growth | 5,000 | 600 req/min | | Pro | 15,000 | 600 req/min | | Scale | Custom | 6,000 req/min | 1 credit = 1 API request. Unused paid credits never expire and roll over month to month. Credits are subject to reasonable use limits to ensure service stability (max balance = 6× monthly allocation; monthly refills are capped, purchased top-ups are not). Webhook deliveries and sandbox requests are free. Rate limit headers on every response: - `X-RateLimit-Limit` — requests allowed per minute - `X-RateLimit-Remaining` — requests remaining - `X-RateLimit-Reset` — UTC epoch seconds when the window resets --- # Endpoints --- ## POST /v0/auth/register Create a new FlowCheck account and get a sandbox API key instantly. No authentication or payment required. Sandbox keys return realistic mock data so you can explore the full API immediately. When ready for live Stripe and bank data, complete checkout via the returned checkout_url. **Request body:** ```json { "email": "dev@example.com" } ``` **Response (201):** ```json { "data": { "registration_id": "tenant_abc123", "api_key": "fc_test_abc123...", "mode": "sandbox", "checkout_url": "https://checkout.stripe.com/...", "message": "Your sandbox API key is ready. Use it immediately..." } } ``` The sandbox key works immediately. Start making API calls right away. --- ## GET /v0/auth/register/status Check registration status and retrieve a production API key after completing checkout. Only needed when you want live Stripe and bank data. **Query parameters:** - `token` (string, required) — registration token from `/v0/auth/register` **Response (200):** ```json { "data": { "status": "active", "api_key": "fc_live_abc123...", "tenant_id": "tenant_abc123" } } ``` Note: The production key is only returned when checkout is complete and only on the first retrieval. --- ## GET /v0/balance Get current Stripe and bank balances in a single call. **Auth:** Bearer token required **Response (200):** ```json { "data": { "stripe": { "available": 142350, "pending": 28900, "currency": "usd" }, "bank": { "available": 138200, "current": 145000, "currency": "usd", "institution": "Chase", "last_updated": "2026-03-05T09:30:00Z" } } } ``` All amounts are in cents (USD). --- ## GET /v0/cashflow Get daily inflow/outflow breakdown. **Auth:** Bearer token required **Query parameters:** - `window` (string, default `"30d"`) — one of `"7d"`, `"30d"`, `"90d"` **Response (200):** ```json { "data": { "window": "30d", "total_inflows": 523400, "total_outflows": 187600, "net": 335800, "daily": [ { "date": "2026-03-05", "inflows": 18200, "outflows": 4300, "net": 13900 } ] } } ``` --- ## GET /v0/payouts List Stripe and Shopify payouts with reconciliation status. **Auth:** Bearer token required **Query parameters:** - `source` (string) — filter by source: `"stripe"` or `"shopify"` - `status` (string) — filter by status: `"paid"`, `"pending"`, `"in_transit"`, `"failed"`, `"canceled"` - `from` (string, ISO date) — start date filter - `to` (string, ISO date) — end date filter - `limit` (integer, default 50) — items per page - `cursor` (string) — pagination cursor **Response (200):** ```json { "data": [ { "id": "po_abc123", "amount": 15000, "currency": "usd", "status": "paid", "arrival_date": "2026-03-03", "created_at": "2026-03-01T10:00:00Z", "reconciliation": { "status": "matched", "confidence": 95, "matched_transaction_id": "txn_xyz789" } } ], "meta": { "request_id": "req_abc", "timestamp": "2026-03-05T10:00:00Z", "version": "v0", "cursor": "eyJpZCI6MTAwfQ", "has_more": true } } ``` --- ## GET /v0/payouts/:id Get a single payout with matched bank transaction details. **Auth:** Bearer token required **URL parameters:** - `id` (string, required) — Stripe payout ID **Response (200):** ```json { "data": { "id": "po_abc123", "amount": 15000, "net_amount": 14850, "fee_amount": 150, "currency": "usd", "status": "paid", "arrival_date": "2026-03-03", "created_at": "2026-03-01T10:00:00Z", "description": "STRIPE PAYOUT", "reconciliation": { "status": "matched", "confidence": 95, "matched_transaction": { "id": "txn_xyz789", "amount": -148.50, "date": "2026-03-03", "description": "STRIPE TRANSFER", "institution": "Chase" } } } } ``` --- ## GET /v0/transactions List bank transactions from Plaid. **Auth:** Bearer token required **Query parameters:** - `from` (string, ISO date) — start date filter - `to` (string, ISO date) — end date filter - `limit` (integer, default 50) — items per page - `cursor` (string) — pagination cursor **Response (200):** ```json { "data": [ { "id": "txn_xyz789", "amount": -148.50, "date": "2026-03-03", "description": "STRIPE TRANSFER", "category": "Transfer", "institution": "Chase", "account_id": "acc_123", "pending": false } ] } ``` Note: Plaid reports deposits as negative amounts. Use `Math.abs(amount)` for comparison. --- ## GET /v0/discrepancies List open discrepancies: missing deposits, amount mismatches, timing issues. **Auth:** Bearer token required **Query parameters:** - `status` (string) — `"open"`, `"resolved"`, `"dismissed"` - `type` (string) — `"missing_deposit"`, `"amount_mismatch"`, `"timing"` - `limit` (integer, default 50) — items per page - `cursor` (string) — pagination cursor **Response (200):** ```json { "data": [ { "id": "disc_abc", "type": "missing_deposit", "status": "open", "payout_id": "po_abc123", "payout_amount": 15000, "expected_date": "2026-03-03", "description": "Stripe payout of $150.00 has no matching bank deposit after 5 business days.", "created_at": "2026-03-05T10:00:00Z" } ] } ``` --- ## GET /v0/reconcile/summary Get 30-day reconciliation summary. **Auth:** Bearer token required **Response (200):** ```json { "data": { "period_start": "2026-02-03", "period_end": "2026-03-05", "total_payouts": 47, "matched": 43, "unmatched": 3, "pending": 1, "total_payout_amount": 823400, "matched_amount": 789200, "open_discrepancies": 2 } } ``` --- ## GET /v0/reconcile/:payout_id Get detailed reconciliation for a specific payout. **Auth:** Bearer token required **URL parameters:** - `payout_id` (string, required) — Stripe payout ID **Response (200):** ```json { "data": { "payout_id": "po_abc123", "payout_amount": 15000, "status": "matched", "confidence": 95, "match_details": { "amount_score": 40, "date_score": 30, "description_score": 20, "bank_id_score": 5, "total_score": 95 }, "matched_transaction": { "id": "txn_xyz789", "amount": -148.50, "date": "2026-03-03", "description": "STRIPE TRANSFER" }, "candidates": [] } } ``` Confidence scoring: amount (40pts), date proximity (30pts), description match (20pts), bank ID (10pts). Auto-match at 80+ with 10pt gap to runner-up. --- ## GET /v0/agent/position Financial summary designed for AI agents and automated systems. **Auth:** Bearer token required **Response (200):** ```json { "data": { "as_of": "2026-03-05T10:00:00Z", "balances": { "stripe_available": 142350, "stripe_pending": 28900, "bank_available": 138200 }, "last_7_days": { "payouts_count": 12, "payouts_total": 187400, "matched": 11, "unmatched": 1, "inflows": 203400, "outflows": 52100 }, "open_discrepancies": 2 } } ``` --- ## GET /v0/agent/alerts Active alerts and discrepancies in agent-friendly format. **Auth:** Bearer token required **Response (200):** ```json { "data": { "alerts": [ { "id": "disc_abc", "severity": "high", "type": "missing_deposit", "summary": "Payout of $150.00 (po_abc123) has no matching bank deposit after 5 days.", "payout_id": "po_abc123", "amount": 15000, "created_at": "2026-03-05T10:00:00Z" } ], "total": 2, "high": 1, "medium": 1, "low": 0 } } ``` --- ## POST /v0/connect/stripe Connect a Stripe account using a restricted API key. **Auth:** Bearer token required **Request body:** ```json { "restricted_key": "rk_live_..." } ``` The restricted key must have read access to Payouts and Balance. FlowCheck encrypts it with AES-256-GCM before storage. **Response (200):** ```json { "data": { "connected": true, "stripe_account_id": "acct_abc123", "permissions": ["payouts.read", "balance.read"] } } ``` --- ## POST /v0/connect/shopify Connect a Shopify store. Orders and payouts will be synced automatically. **Auth:** Bearer token required **Request body:** ```json { "shop": "my-store.myshopify.com", "access_token": "shpat_..." } ``` The access token must have read access to Orders and Payouts. FlowCheck encrypts it with AES-256-GCM before storage. **Response (200):** ```json { "data": { "connected": true, "shop": "my-store.myshopify.com", "connected_at": "2026-03-05T10:00:00Z" } } ``` --- ## POST /v0/connect/plaid/link-token Create a Plaid Link token to initiate bank connection in the browser. **Auth:** Bearer token required **Response (200):** ```json { "data": { "link_token": "link-sandbox-abc123", "expiration": "2026-03-05T14:00:00Z" } } ``` Use this token with the Plaid Link SDK in the browser to let the user select their bank. --- ## POST /v0/connect/plaid/exchange Exchange a Plaid public token (from Link) for a permanent access token. **Auth:** Bearer token required **Request body:** ```json { "public_token": "public-sandbox-abc123" } ``` **Response (200):** ```json { "data": { "connected": true, "institution": "Chase", "account_name": "Checking ****1234" } } ``` --- ## GET /v0/webhooks List registered webhook endpoints. **Auth:** Bearer token required **Response (200):** ```json { "data": [ { "id": "wh_abc123", "url": "https://example.com/webhooks/flowcheck", "events": ["payout.matched", "discrepancy.created"], "active": true, "created_at": "2026-03-01T10:00:00Z" } ] } ``` --- ## POST /v0/webhooks Register a new webhook endpoint. **Auth:** Bearer token required **Request body:** ```json { "url": "https://example.com/webhooks/flowcheck", "events": ["payout.matched", "discrepancy.created", "balance.threshold"] } ``` URL must be HTTPS. A signing secret is returned for HMAC verification. **Response (201):** ```json { "data": { "id": "wh_abc123", "url": "https://example.com/webhooks/flowcheck", "events": ["payout.matched", "discrepancy.created", "balance.threshold"], "signing_secret": "whsec_abc123...", "active": true } } ``` --- ## DELETE /v0/webhooks/:id Remove a webhook endpoint. **Auth:** Bearer token required **URL parameters:** - `id` (string, required) — webhook endpoint ID **Response (200):** ```json { "data": { "deleted": true } } ``` --- ## Webhook Signature Verification FlowCheck signs webhook payloads using HMAC-SHA256 (same pattern as Stripe): Header format: ``` FlowCheck-Signature: t=1709640000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd ``` Verification steps: 1. Extract `t` (timestamp) and `v1` (signature) from the header 2. Build signed payload: `${timestamp}.${raw_body}` 3. Compute HMAC-SHA256 of the signed payload using your signing secret 4. Compare computed signature to `v1` (constant-time comparison) 5. Check that `t` is within 5 minutes of current time to prevent replay attacks Retry schedule: immediate, 5m, 30m, 2h, 8h, 24h (max 6 attempts). After 6 consecutive failures, the endpoint is automatically disabled. --- ## Webhook Event Types - `payout.matched` — a payout (Stripe or Shopify) was matched to a bank deposit - `payout.discrepancy` — a matched payout has an amount discrepancy - `payout.missing` — a payout could not be matched after the expected window - `refund.detected` — a Shopify refund was detected that may affect reconciliation - `balance.threshold` — a balance crossed a configured threshold --- ## POST /v0/sync Trigger a full sync of all connected integrations and run the reconciliation engine. **Auth:** Bearer token required **Rate limit:** 1 sync per 5 minutes per tenant. Returns `429` if called too soon. **Request body:** None required. **Response (200):** ```json { "data": { "synced": ["stripe", "plaid"], "skipped": ["shopify"], "stripe": { "payouts_synced": 3 }, "shopify": { "orders_synced": 0, "refunds_detected": 0 }, "plaid": { "added": 7, "modified": 1, "removed": 0 }, "reconciliation": { "matched": 2, "issues_detected": 1 } } } ``` `synced` lists integrations that were synced. `skipped` lists integrations not connected. At least one integration must be connected. **Errors:** - `400 missing_parameter` — No integrations connected. - `429 rate_limited` — Sync already ran within the last 5 minutes. --- ## POST /v0/billing/topup Buy 100 API credits for $5. Charges the card on file immediately. **Auth:** Bearer token required **Request body:** None required. **Response (200):** ```json { "data": { "credits_added": 100, "credits_remaining": 947, "amount_charged": 500, "currency": "usd", "invoice_id": "in_1abc..." } } ``` **Errors:** - `400 missing_parameter` — No payment method on file. Complete subscription setup first. - `402 internal_error` — Payment declined. Update payment method in dashboard. --- ## POST /v0/billing/upgrade Create a Stripe Checkout session to upgrade your plan. Works at 0 credits (no credit deducted). **Auth:** Bearer token required **Request body:** ```json { "plan": "starter" } ``` **Parameters:** - `plan` (string, required) — `"starter"`, `"growth"`, or `"pro"` **Response (200):** ```json { "data": { "checkout_url": "https://checkout.stripe.com/c/pay/cs_...", "plan": "starter", "credits": 1000, "price_monthly": "$4.99" } } ``` If you already have an active subscription, this returns a billing portal URL where you can change plans. --- ## Credit System Every API response includes `meta.credits_remaining` so you can monitor your balance programmatically. When credits reach zero, the API returns a plan-aware `402` with actionable next steps: **Trial / expired users (no subscription):** ```json { "data": { "actions": { "upgrade": { "method": "POST", "url": "/v0/billing/upgrade", "body": { "plan": "starter | growth | pro" }, "plans": [ { "id": "starter", "credits": 1000, "price_monthly": "$4.99" }, { "id": "growth", "credits": 5000, "price_monthly": "$19" }, { "id": "pro", "credits": 15000, "price_monthly": "$49" } ] } } }, "errors": [{ "status": 402, "code": "credits_exhausted", "detail": "Your credit balance is zero. Upgrade your plan via POST /v0/billing/upgrade to continue." }] } ``` **Paid users (active subscription):** ```json { "data": { "actions": { "topup": { "method": "POST", "url": "/v0/billing/topup", "description": "Add 100 credits for $5.00" } } }, "errors": [{ "status": 402, "code": "credits_exhausted", "detail": "Your credit balance is zero. Top up via POST /v0/billing/topup or at /dashboard/billing." }] } ``` Both `POST /v0/billing/upgrade` and `POST /v0/billing/topup` work programmatically. The upgrade endpoint works even at 0 credits. --- ## Pricing | Plan | Price | Credits/mo | Rate Limit | Plaid Connections | |------|-------|-----------|------------|-------------------| | Trial | Free (7 days) | 100 | 60/min | 1 included | | Starter | $4.99/mo | 1,000 | 300/min | 1 included | | Growth | $19/mo | 5,000 | 600/min | 1 included | | Pro | $49/mo | 15,000 | 600/min | 1 included | | Scale | Custom | Custom | 6,000/min | Custom | Credit top-up: 100 credits for $5 (via API or dashboard). Unused paid credits never expire and roll over month to month. Each plan has a maximum credit balance of 6× the monthly allocation (e.g. Starter: 6,000, Growth: 30,000, Pro: 90,000). Monthly refills are capped at this limit; purchased top-ups are not. Additional Plaid connections: $1/mo each. Webhook deliveries are free. Sandbox usage is always free.