AEVIONTrust · IP · Globus
DemoAuthQRightQSignBureauPlanetAwardsBankChessPricingAPI
developers / fintech

AEVION Fintech API — Developer reference

33+ REST endpoints across 6 modules. Shared JWT auth. PostgreSQL-backed. Production-ready and live on api.aevion.app.

Quickstart

→ Interactive quickstart: 6 curl examples with copy-buttons → · SDK reference → · Webhooks → · Error codes → · Troubleshooting → · Rate limits → · Code examples → · Migration guide → · Changelog → · API Playground →

Every fintech module gates writes behind the platform JWT. Bootstrap a token once, then use it as Authorization: Bearer <token> on subsequent calls. Read endpoints are unauthenticated where the resource is public (campaign listings, chain head, leaderboards) and JWT-bound everywhere else.

# 1. Register (or POST /api/auth/login if you already have an account)
curl -X POST https://api.aevion.app/api/auth/register \
  -H 'Content-Type: application/json' \
  -d '{ "email": "you@example.com", "password": "supersecret" }'

# Response: { "token": "<JWT>", "user": { "id": "...", "email": "..." } }

# 2. Export the token and call any fintech endpoint
export AEV_TOKEN='<JWT>'
curl https://api.aevion.app/api/qpaynet/me/dashboard \
  -H "Authorization: Bearer $AEV_TOKEN"

Conventions

  • All bodies and responses are JSON. Content-Type: application/json required on writes.
  • Money is sent and returned in minor units (KZT tiyin, USD cents). Field name is amountKzt / amountCents; never floats.
  • Idempotency: Idempotency-Key header on POST /qpaynet/transfer, /withdraw, /deposit— replays return the original transaction.
  • Pagination: ?cursor=&limit= on list endpoints, capped at 200. Response includes nextCursor when more results exist.
  • Rate limits: per-IP read 240/min, write 20-60/min, money-moving 30/min.429 on overflow, RateLimit-* headers always present.
  • Errors: { "error": "code", "message": "..." } with accurate HTTP status (400 / 401 / 403 / 404 / 409 / 422 / 429 / 500).

1. QPayNet — fiat wallet rails

End-to-end fiat wallet system with Stripe-backed deposits, P2P transfers, payouts, merchant charge API, payment requests with public pay pages, KYC, refunds, admin reconciliation, and a real-time SSE event stream. The largest fintech surface area in the platform — /api/qpaynet owns the ledger of record for KZT/USD.

Auth model
  • JWT bearer on every owner-scoped route. Admin routes (/admin/*) additionally require the caller email to appear in QPAYNET_ADMIN_EMAILS.
  • Merchant API keys (issued via POST /merchant/keys) authenticate POST /merchant/charge via X-API-Key— no JWT needed for server-to-server billing.
  • Public pages/wallets/:id/public, /requests/:token— are rate-limited but unauthenticated.
  • Stripe webhook/deposit/webhook validates the Stripe-Signature header against QPAYNET_STRIPE_WEBHOOK_SECRET.

Key endpoints

MethodPathDescription
POST/api/qpaynet/walletsCreate a wallet (one per currency per user).
GET/api/qpaynet/walletsList your wallets with balances.
GET/api/qpaynet/wallets/:id/publicPublic-safe view of a wallet for receive links.
POST/api/qpaynet/deposit/checkoutCreate a Stripe Checkout session for fiat top-up.
POST/api/qpaynet/deposit/webhookStripe-signed webhook that credits the wallet on payment_intent.succeeded.
POST/api/qpaynet/withdrawInitiate an off-platform payout. Triggers VeilNetX entry + Z-Tide event.
POST/api/qpaynet/transferP2P transfer between two wallets. 0.1% platform fee, idempotent.
GET/api/qpaynet/transactions[.csv]Paginated transaction history. CSV export available.
POST/api/qpaynet/merchant/keysIssue a server-to-server API key for charge collection.
POST/api/qpaynet/merchant/chargeCharge a customer wallet using the merchant API key.
POST/api/qpaynet/requestsCreate a payment-request link. Returns shareable token + pay URL.
POST/api/qpaynet/requests/:token/payPay a request from the authenticated user's wallet.
POST/api/qpaynet/payoutsRequest an external bank payout (admin-reviewed).
POST/api/qpaynet/kyc/submitSubmit KYC docs to lift caps. Cached threshold $1m KZT/month.
POST/api/qpaynet/webhook-subsSubscribe to balance/transfer/request events. HMAC-signed POSTs.
GET/api/qpaynet/me/dashboardAggregated wallet + tx + request snapshot for the current user.
GET/api/qpaynet/admin/auditAppend-only audit log of state changes (admin).
GET/api/qpaynet/admin/reconcileReconcile Stripe charges vs internal ledger (admin).
GET/api/qpaynet/admin/eventsSSE stream of fintech events for ops dashboards.
GET/api/qpaynet/openapi.jsonAuto-generated OpenAPI 3.0 spec for the whole router.
GET/api/qpaynet/healthLiveness + Postgres reachability + active wallet count.

Example — P2P transfer with idempotency

curl -X POST https://api.aevion.app/api/qpaynet/transfer \
  -H "Authorization: Bearer $AEV_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: transfer-2026-05-10-001' \
  -d '{
    "fromWalletId": "wal_8f2a...",
    "toEmail":      "alice@example.com",
    "amountKzt":    50000,
    "memo":         "Coffee debt"
  }'

# 200 OK
# {
#   "transactionId": "tx_b3c1...",
#   "feeKzt":         50,
#   "fromBalanceKzt": 1249950,
#   "toBalanceKzt":   620050,
#   "createdAt":      "2026-05-10T14:33:08Z"
# }
Env vars
  • STRIPE_SECRET_KEY — Stripe live/test secret for Checkout sessions.
  • QPAYNET_STRIPE_WEBHOOK_SECRET — raw whsec_... for signature verification.
  • QPAYNET_ADMIN_EMAILS — comma-separated allowlist for /admin/*.
  • QPAYNET_DAILY_DEPOSIT_CAP (default 500000) — per-user daily KZT cap pre-KYC.
  • QPAYNET_MAX_TRANSFER (default 100000) — per-tx KZT cap pre-KYC.
  • QPAYNET_KYC_THRESHOLD (default 1000000) — monthly cumulative KZT before KYC mandatory.
  • QPAYNET_PII_SALT — HMAC salt for redacted audit log lookups.
  • SMTP_HOST / SMTP_USER / SMTP_PASS / SMTP_FROM — transaction email delivery.
  • FRONTEND_URL — base URL embedded in receipt + request links.
  • QPAYNET_ALERT_URL — webhook target for reconcile drift > 0.
Cross-product hooks
  • POST /withdraw — emits VeilNetX-Ledger withdrawal entry + Z-Tide event qpaynet-payout (+3).
  • POST /transfer — emits VeilNetX-Ledger transfer entry; sender and receiver each receive Z-Tide qpaynet-active (+1).
  • POST /deposit/webhookon Stripe success — appends VeilNetX deposit entry + Z-Tide qpaynet-deposit (+2).

2. QMaskCard — virtual payment masks

Meta-tokenization layer that sits between users and the real card rail. Each mask is a unique aev-mask-<32hex>sentinel with its own spend limit, optional merchant-lock or category-lock, and per-charge AI risk score. Merchants never see real PAN/CVV — the mask routes to QPayNet / Stripe for settlement.

Auth model
  • JWT bearer on every endpoint. Masks are bound to userId — no admin allowlist; users can only see and revoke their own masks.
  • Charges are authorized server-side by the merchant integration after fraud-score evaluation. Hard declines for risk ≥ 80.

Key endpoints

MethodPathDescription
GET/api/qmaskcard/healthLiveness + DB reachability.
POST/api/qmaskcard/masksIssue a virtual mask. Kinds: single-use, recurring, merchant-locked, category-locked.
GET/api/qmaskcard/masksList your active masks with remaining balance.
POST/api/qmaskcard/masks/:id/revokeSoft-revoke a mask. Subsequent charges declined.
POST/api/qmaskcard/chargesAuthorize a charge against a mask. Computes AI risk score 0..100.
GET/api/qmaskcard/chargesList charges, optional ?maskId= filter.
GET/api/qmaskcard/statsRoll-up: active masks, total spend, decline rate, mean risk score.

Example — issue a merchant-locked mask

curl -X POST https://api.aevion.app/api/qmaskcard/masks \
  -H "Authorization: Bearer $AEV_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "label":            "Spotify monthly",
    "kind":             "merchant-locked",
    "lockedToMerchant": "spotify.com",
    "currency":         "USD",
    "spendLimitCents":  1200,
    "expiresAt":        "2027-01-01T00:00:00Z"
  }'

# 201 Created
# {
#   "id":              "msk_4f1a...",
#   "virtualPan":      "aev-mask-3c1f9a8e8b1c4d5e9f0a1b2c3d4e5f60",
#   "remainingCents":  1200,
#   "status":          "active"
# }
Env vars
  • None mask-specific beyond shared DATABASE_URL and AUTH_JWT_SECRET. Settlement-side env (STRIPE_SECRET_KEY) lives in QPayNet.
Cross-product hooks
  • POST /charges when status: authorized— emits VeilNetX-Ledger mask-charge entry. Decremented balance settles via QPayNet wallet under the hood (when configured).
  • High-risk charges (score ≥ 70) emit a Z-Tide qmaskcard-risk-flag event (-2) on the merchant's userId.

3. VeilNetX-Ledger — settlement chain

Append-only, hash-chained ledger of every fintech state change across the platform. Each entry carries a SHA-256 of the previous head — tampering is detectable end-to-end via GET /chain/verify. The chain is the canonical evidence trail for audits, refunds, and compliance reviews.

Auth model
  • JWT bearer for POST /entries — only platform-internal services (called from inside other routers) emit entries; user code does not write directly.
  • Reads (/entries, /chain/head, /chain/verify, /stats) are public-safe and require no auth — tampering visibility is a feature.

Key endpoints

MethodPathDescription
GET/api/veilnetx-ledger/healthLiveness + last entry timestamp.
POST/api/veilnetx-ledger/entriesAppend a new entry. Computes prevHash chain link server-side.
GET/api/veilnetx-ledger/entriesList entries with ?kind= / ?actorUserId= / ?since= filters.
GET/api/veilnetx-ledger/entries/:idRetrieve a single entry with its hash chain context.
GET/api/veilnetx-ledger/chain/headCurrent head: id, hash, height, timestamp.
GET/api/veilnetx-ledger/chain/verifyWalks the chain start to head, returns first invalid link or { valid: true, height }.
GET/api/veilnetx-ledger/statsRolling 24h/7d/30d entry counts by kind.

Example — verify chain integrity

curl https://api.aevion.app/api/veilnetx-ledger/chain/verify

# 200 OK
# {
#   "valid":        true,
#   "height":       18429,
#   "headHash":     "9c2e8f...3b1a",
#   "verifiedAt":   "2026-05-10T14:35:22Z",
#   "msToVerify":   142
# }

# If tampered:
# { "valid": false, "brokenAt": 8112, "expectedPrevHash": "...", "foundPrevHash": "..." }
Env vars
  • VEILNETX_SALT — HMAC salt mixed into every hash. Required in production (process refuses to boot without it). Falls back to SHARD_HMAC_SECRET for legacy installs.
Cross-product hooks
  • Acts as the sink, not a source. Receives entries from QPayNet (deposit/withdraw/transfer), QMaskCard (charge), QGood (donation), QChainGov (proposal-close).
  • Bureau-grade certificate emission references chain/head at certificate-issue time as a notarization anchor.

4. Z-Tide — reputation layer

Cross-product reputation system. Every user has a single score (signed int) and a rank label (seedling / sprout / steady / strong / titan). Events from any module add weighted deltas; ranks promote/demote at fixed thresholds. Designed for inbound writes from the platform — not directly from user code.

Auth model
  • Service-key OR admin JWT for POST /events. Service mode: X-ZTide-Service-Key: $ZTIDE_SERVICE_KEY. Admin mode: caller email in ZTIDE_ADMIN_EMAILS.
  • Open reads: /leaderboard, /rank/:userId, /stats are public.
  • GET /me requires a JWT bearer — returns the caller's score with recent events.

Key endpoints

MethodPathDescription
GET/api/ztide/healthLiveness + ranked-user count.
POST/api/ztide/eventsIngest a reputation event { userId, kind, sourceModule, weight, meta }.
GET/api/ztide/meAuthenticated user's score, rank, last 50 events.
GET/api/ztide/leaderboardTop scores. ?limit= up to 100, ?rank= filter, ?since= window.
GET/api/ztide/rank/:userIdPublic rank + score for a user (no PII).
GET/api/ztide/statsDistribution: count per rank, mean/median score, events ingested 24h.

Example — ingest a reputation event (service-key mode)

curl -X POST https://api.aevion.app/api/ztide/events \
  -H "X-ZTide-Service-Key: $ZTIDE_SERVICE_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "userId":        "usr_8a2f1b...",
    "kind":          "qpaynet-payout",
    "sourceModule":  "qpaynet",
    "weight":        3,
    "meta":          { "transactionId": "tx_b3c1...", "amountKzt": 50000 }
  }'

# 201 Created
# {
#   "eventId":      "evt_4f1a...",
#   "newScore":     142,
#   "previousRank": "sprout",
#   "newRank":      "steady",
#   "promoted":     true
# }
Env vars
  • ZTIDE_SERVICE_KEY — 32+ char secret for backend-to-backend ingestion. Compared with crypto.timingSafeEqual.
  • ZTIDE_ADMIN_EMAILS — comma-separated allowlist that can ingest events via JWT instead of service key.
Cross-product hooks
  • Pure sink. Every other fintech module ingests events here on state changes (transfer, deposit, donation, vote, mask-charge). Score promotions are surfaced back to the user dashboard via the QPayNet notifications router.

5. QChainGov — on-chain governance

Proposal-vote-execute lifecycle for protocol/treasury/module/partnership decisions. Three vote modes: yes-no-abstain, ranked-choice, weighted. Proposals lock atopen, tally at close, append an entry to VeilNetX-Ledger on outcome.

Auth model
  • JWT bearer for all writes. Anyone can POST /proposals (as draft); open and close require QCHAINGOV_ADMIN_EMAILS.
  • One vote per (proposalId, userId); idempotency enforced at the unique index level.
  • Reads are public: anyone can list proposals, view votes-so-far, see results.

Key endpoints

MethodPathDescription
GET/api/qchaingov/healthLiveness + open-proposal count.
POST/api/qchaingov/proposalsCreate a draft proposal: title, summary, body, category, voteMode, quorum, threshold.
GET/api/qchaingov/proposalsList with ?status= / ?category= / ?authorUserId= filters.
GET/api/qchaingov/proposals/:idSingle proposal with current tally and vote breakdown.
POST/api/qchaingov/proposals/:id/votesCast a vote. Body shape depends on voteMode.
POST/api/qchaingov/proposals/:id/openAdmin: move draft to open. Sets votesOpenAt / votesCloseAt.
POST/api/qchaingov/proposals/:id/closeAdmin: tally votes, set executed/rejected, append VeilNetX entry.
GET/api/qchaingov/proposals/:id/votesPer-voter ballots for closed proposals (public, no PII besides userId).
GET/api/qchaingov/statsTotal proposals by status, mean turnout, top categories.

Example — cast a yes-no-abstain vote

curl -X POST https://api.aevion.app/api/qchaingov/proposals/prop_8c1a2f/votes \
  -H "Authorization: Bearer $AEV_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "choice": "yes",
    "reason": "Aligns with the treasury policy ratified Q1 2026."
  }'

# 201 Created
# {
#   "voteId":      "vote_a1b2c3...",
#   "tallyNow":    { "yes": 87, "no": 12, "abstain": 3 },
#   "quorumMet":   true,
#   "passing":     true
# }
Env vars
  • QCHAINGOV_ADMIN_EMAILS — comma-separated allowlist that can /open and /close proposals.
Cross-product hooks
  • POST /proposals/:id/close — emits VeilNetX-Ledger governance-decision entry with full tally + Z-Tide qchaingov-author (+5 if proposal passed, +1 if rejected with quorum, 0 if no quorum).
  • POST /proposals/:id/votes — emits Z-Tide qchaingov-voter (+1) on first vote of the proposal.

6. QGood — charity campaigns

Public campaign listings with admin moderation, donations via QPayNet wallets, transparent on-chain donation receipts (VeilNetX entry per donation). Bonus 6th module — reuses the platform's identity and fintech rails rather than running parallel infra.

Auth model
  • Public readsGET /campaigns and GET /campaigns/:id need no auth. Used for the marketing site.
  • JWT bearer for POST /campaigns (anyone can propose) and POST /campaigns/:id/donations (donor must be authenticated).
  • POST /campaigns/:id/approve requires caller email in QGOOD_ADMIN_EMAILS — otherwise campaigns stay pending and invisible to public reads.

Key endpoints

MethodPathDescription
GET/api/qgood/healthLiveness + active-campaign count.
GET/api/qgood/campaignsPublic list of approved campaigns. ?category= / ?status= / cursor pagination.
GET/api/qgood/campaigns/:idSingle campaign with progress, recent donors (opt-in), media.
POST/api/qgood/campaignsPropose a campaign. Returns id with status=pending until admin approves.
POST/api/qgood/campaigns/:id/approveAdmin: flip pending → active. Optionally pin or feature.
POST/api/qgood/campaigns/:id/donationsDonate from a QPayNet wallet. Settles atomically + emits VeilNetX receipt.
GET/api/qgood/statsTotal raised across campaigns, top causes, donor count.

Example — donate to an active campaign

curl -X POST https://api.aevion.app/api/qgood/campaigns/cmp_2f1a8b/donations \
  -H "Authorization: Bearer $AEV_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "walletId":   "wal_8f2a...",
    "amountKzt":  10000,
    "anonymous":  false,
    "message":    "For the kids. Keep going."
  }'

# 201 Created
# {
#   "donationId":      "don_4f1a...",
#   "ledgerEntryId":   "vnx_b3c1...",
#   "transactionId":   "tx_a1b2...",
#   "campaignTotalKzt": 8420000,
#   "thankYouUrl":     "https://aevion.app/qgood/cmp_2f1a8b/thanks/don_4f1a..."
# }
Env vars
  • QGOOD_ADMIN_EMAILS — comma-separated allowlist that can /approve pending campaigns.
Cross-product hooks
  • POST /campaigns/:id/donations— debits the donor's QPayNet wallet, credits the campaign treasury wallet, appends a VeilNetX-Ledger donation entry, and emits Z-Tide qgood-donor (+4).
  • On campaign approval — emits Z-Tide qgood-organizer (+2) to the proposer.

OpenAPI spec

The full machine-readable spec for all six modules is generated from the backend at build time and served at GET /api/openapi.json. The source of truth lives in:

aevion-globus-backend/src/lib/openapiFintechSpec.ts

Editing that file extends or amends the surface for any of the six modules. The frontend Stoplight UI at /developers/openapi consumes it directly — no separate sync step. Per-module specs (/api/qpaynet/openapi.json etc.) are scoped slices of the same spec, useful for partner integrations that only touch one module.

Smoke testing

Each fintech module ships a black-box smoke script that exercises its critical path against a running backend (local or production). Combined runner:

# Local: start the backend first
cd aevion-globus-backend && npm run dev   # leaves http://localhost:4001 running

# Then in another shell, from the monorepo root:
npm run smoke:all

# Or run a single module
npm run smoke:qpaynet
npm run smoke:qmaskcard
npm run smoke:veilnetx-ledger
npm run smoke:ztide
npm run smoke:qchaingov
npm run smoke:qgood

# Production: override the base URL
SMOKE_BASE=https://api.aevion.app npm run smoke:all

Individual scripts live in aevion-globus-backend/scripts/:

  • scripts/qpaynet-smoke.js — 21 steps: wallet, deposit-stub, transfer, request, payout, webhook delivery, admin reconcile.
  • scripts/qmaskcard-smoke.js — 7 steps: issue mask, authorize charge, revoke, fraud-score boundary.
  • scripts/veilnetx-ledger-smoke.js — 5 steps: append entries, fetch head, verify chain integrity.
  • scripts/ztide-smoke.js — 6 steps: service-key auth, ingest events, leaderboard pagination, rank promotion.
  • scripts/qchaingov-smoke.js — 9 steps: propose, open, vote x3, close, verify tally + VeilNetX entry.
  • scripts/qgood-smoke.js — 6 steps: propose, approve, donate, refund, verify ledger trail.
  • scripts/fintech-cross-module-smoke.mjs — 7 steps: cross-module health audit (all 5 fintech modules).
  • scripts/fintech-onboarding-guide.mjs — integrator walk-through; --probe hits live health endpoints.
  • scripts/fintech-stats-aggregator.mjs — CLI stats dashboard; --json emits machine-readable.
  • scripts/fintech-uptime-monitor.mjs — long-running monitor with p50/p95 latency.
  • scripts/fintech-weekly-report.mjs — markdown weekly digest.

Smoke failures exit non-zero and dump the failing step + response body. CI runs the suite on every push against a Postgres + Stripe-test backend; production smokes run post-deploy against api.aevion.app.