AlphaFlow/Documentation

Documentation

AlphaFlow's headline capability is snapshot sybil screening: submit an airdrop claimant list, get back the clusters of wallets operated together, with the on-chain evidence behind every flag.

Getting Started

  1. Generate a key from API Keys in the dashboard
  2. POST /api/sybil/check with your claimant list — you get back a jobId
  3. Poll GET /api/sybil/jobs/:id until status is complete
  4. Pull the forwardable summary or CSV from /api/sybil/jobs/:id/report

Prefer not to write code? Snapshot Screening in the dashboard runs the same pipeline through a UI.

Authentication

Two accepted credentials, checked in this order: an af_ API key, then the dashboard session cookie. Session users are treated as DEVELOPER tier and aren't counted against any key's quota.

Authorization: Bearer af_your_key_here
Job polling is scoped to whoever enqueued the job. Another account polling your job id gets a 404, not someone else's results.

OpenAPI Spec

The full route inventory — every endpoint, method, and tier requirement — is available as a machine-readable OpenAPI 3.0 document, generated from the same route source this page used to be hand-transcribed from. Import it directly into Postman, Insomnia, or Swagger UI.

GET /api/openapi.json
Most endpoints are listed with method, path, auth, and tier — accurate, but not every route has a full request/response schema filled in yet. Endpoints without one say so in their description field rather than guessing at a shape.

Response Shape

Every successful response is wrapped as { "data": ... }, with pagination or other response context under a sibling meta key when present — data is always exactly the resource you asked for, never mixed with metadata about the response itself. Errors are unchanged: { "error": "..." } with a non-2xx status. The CSV report format is the one exception — it returns a raw CSV body, not JSON.

This is a breaking change from an earlier version of this API, which returned each endpoint's payload unwrapped. If a response you're parsing looks like it's missing fields, check whether you need to read them from response.data now.

Async Jobs

The three forensics endpoints are enqueue-only. A 500-wallet snapshot fans out to thousands of on-chain lookups, so the work runs on a background worker rather than inside the request. POST → 202 {jobId, poll} → poll until complete or failed.

POST/api/sybil/checkDEVELOPER+

Snapshot sybil screening — cluster an airdrop claimant list into wallets operated together

body: { "addresses": [ "<base58>", ... ] }

GET/api/sybil/jobs/:id
POST/api/consolidation/scanDEVELOPER+

Trace claimant wallets forward to shared sink addresses (farm treasuries and CEX deposit accounts)

body: { "addresses": [ "<base58>", ... ] }

GET/api/consolidation/jobs/:id
POST/api/fundflow/traceDEVELOPER+

Trace outbound fund flow from any address (exploit / rug tracing)

body: { "address": "<base58>", "sinceTimestamp": 1719792000 }

GET/api/fundflow/jobs/:id

Status moves pending processing complete or failed. Poll every few seconds; there's no webhook yet.

Batch caps per request

Separate from the daily rate limit below — this caps addresses in one call, because addresses are the real unit of upstream cost. Applies to /sybil/check and /consolidation/scan.

FREENo access — DEVELOPER tier or above required
DEVELOPER100 addresses per request
GROWTH500 addresses per request
INTERNAL500 addresses per request

Snapshot Screening

1 — Enqueue the snapshot:

curl -X POST https://getalphaflow.xyz/api/sybil/check \
  -H "Authorization: Bearer af_..." \
  -H "Content-Type: application/json" \
  -d '{"addresses": ["Wallet1...", "Wallet2...", "Wallet3..."]}'

# 202 Accepted
# {
#   "data": {
#     "jobId": "clx7f2k9a0001abcd",
#     "status": "pending",
#     "submitted": 3,
#     "poll": "/api/sybil/jobs/clx7f2k9a0001abcd"
#   }
# }

2 — Poll until it completes:

curl https://getalphaflow.xyz/api/sybil/jobs/clx7f2k9a0001abcd \
  -H "Authorization: Bearer af_..."

# while status is "pending" or "processing", wait and poll again.
# on "complete", data holds the result (every success response is
# wrapped in { data }, with optional pagination/context under meta):
# {
#   "data": {
#     "status": "complete",
#     "submitted": 3,
#     "flaggedCount": 2,
#     "clusters": [
#       {
#         "confidence": 0.82,
#         "signals": ["shared_funder", "behavior_match"],
#         "memberCount": 2,
#         "sharedFunder": "Funder...",
#         "members": ["Wallet1...", "Wallet2..."],
#         "smartMoneyMembers": [],
#         "evidence": [
#           {
#             "address": "Wallet1...",
#             "funderAddress": "Funder...",
#             "fundingTxSignature": "5xY...",
#             "commonFeePayer": "Payer...",
#             "behaviorFingerprint": "a91c..."
#           }
#         ]
#       }
#     ],
#     "knownFarmers": [
#       { "address": "Wallet2...", "timesFlagged": 3, "confidence": 0.88 }
#     ],
#     "unclustered": ["Wallet3..."]
#   }
# }

Response fields

signals[]

Per cluster — which detectors fired: shared_funder, shared_fee_payer, behavior_match, timing_sync, funding_burst, age_batch, known_farm_wallet

evidence[]

Per cluster member — { address, funderAddress, firstFundedAt, fundingTxSignature, commonFeePayer, behaviorFingerprint }. The receipts that make an exclusion defensible in public

knownFarmers[]

Submitted addresses that earlier screenings already flagged, with timesFlagged and confidence. Populated whether or not this run clustered them again

band / bandLabel

Per cluster, on the report endpoint — likely_organic (<0.5), needs_review (0.5–0.75), high_confidence (>=0.75). Maps the raw score onto the decision you actually have to make. Note that 0.6, the threshold at which a cluster is recorded to cross-job memory, sits INSIDE needs_review: strong enough to remember, not strong enough to exclude on alone

smartMoneyMembers

Per cluster — members with a verified profitable trading history. Their presence LOWERS the cluster confidence; review these before excluding them

Confidence is evidence strength, not proof. Treat high-confidence clusters as a review queue — the band field states this directly per cluster. Check smartMoneyMembers before excluding anyone: those wallets have a real trading record behind them.

Reports

GET /api/sybil/jobs/:id/report turns a completed job into something you can forward or feed to tooling. JSON gives counts, clusters sorted by confidence, a bands breakdown, and a plain-English summary paragraph; ?format=csv gives one row per flagged address. Non-complete jobs return the same status shape as the poll route, so a client can point at this endpoint the whole way through.

# JSON summary — the shape you forward to a protocol team
curl https://getalphaflow.xyz/api/sybil/jobs/clx7f2k9a0001abcd/report \
  -H "Authorization: Bearer af_..."

# CSV — one row per flagged address, for your exclusion tooling
curl "https://getalphaflow.xyz/api/sybil/jobs/clx7f2k9a0001abcd/report?format=csv" \
  -H "Authorization: Bearer af_..." \
  -o sybil-report.csv

# address,clusterIndex,confidence,signals,funderAddress,fundingTxSignature,band

Full enqueue → poll → report loop:

JOB=$(curl -s -X POST https://getalphaflow.xyz/api/sybil/check \
  -H "Authorization: Bearer $AF_KEY" \
  -H "Content-Type: application/json" \
  -d @snapshot.json | jq -r .data.jobId)

until [ "$(curl -s https://getalphaflow.xyz/api/sybil/jobs/$JOB \
  -H "Authorization: Bearer $AF_KEY" | jq -r .data.status)" = "complete" ]; do
  sleep 5
done

curl -s "https://getalphaflow.xyz/api/sybil/jobs/$JOB/report?format=csv" \
  -H "Authorization: Bearer $AF_KEY" -o sybil-report.csv

Bulk Export

The report endpoints above cover one job, case, or wallet at a time. For everything at once, two list-level export endpoints return a summary row per record rather than each record's full detail — pull the per-record report separately for anything that needs deeper inspection.

GET /api/cases/export?format=csv
GET /api/sybil/jobs/export?format=csv
Both default to JSON; ?format=csv returns a raw CSV body. There is no bulk PDF — per-case and per-job PDF already exist at their individual report endpoints, and a combined multi-record PDF wasn't built (documented as a gap, not a silent omission).

Other Forensics

Consolidation scan — trace known claimants forward to the addresses they swept into. Sinks receiving from 3+ claimants surface as farm treasuries; a sink that is itself a labeled CEX deposit address is its own tell.

curl -X POST https://getalphaflow.xyz/api/consolidation/scan \
  -H "Authorization: Bearer af_..." \
  -H "Content-Type: application/json" \
  -d '{"addresses": ["Claimant1...", "Claimant2..."]}'

# then: GET /api/consolidation/jobs/<jobId>
# result: { claimantsScanned, sinks: [{ address, inflowCount, totalSol,
#           fromClaimants, knownDestination }], unresolved }

Fund-flow trace — follow outbound SOL from any root address, flagging hops that land on known CEX/bridge destinations.

curl -X POST https://getalphaflow.xyz/api/fundflow/trace \
  -H "Authorization: Bearer af_..." \
  -H "Content-Type: application/json" \
  -d '{"address": "SourceWallet..."}'

# then: GET /api/fundflow/jobs/<jobId>

Wallet API

Synchronous reads — these return results inline, no job needed.

GET/api/wallet/:addressFREE+

Wallet score and stats

GET/api/wallet/:address/intelligenceFREE+

Full profile — reason codes, reputation, risk, fund flow, relationships, token activity, outcomes

GET/api/wallet/:address/reputationFREE+

Allow / review / block recommendation

GET/api/wallet/:address/historyFREE+

Wallet's early-buy trading history

GET/api/wallet/:address/outcomesFREE+

Outcomes of every alert fired on this wallet

GET/api/wallet/:address/sybilFREE+

Read prior sybil cluster membership (no new detection)

GET/api/wallet/:address/token-riskFREE+

Wallet's own rug exposure from tokens it has bought

GET/api/wallet/:address/fundflowDEVELOPER+

Trace outbound fund flow from this wallet (synchronous convenience read)

GET/api/token-risk/:mintDEVELOPER+

Scan a token's mint authority, LP lock, holder concentration

GET/api/token/:mint/lineageDEVELOPER+

Mint metadata plus a best-effort creation event and mint/burn candidates

GET/api/token/:mint/concentrationDEVELOPER+

Point-in-time top1/top5/top10 holder concentration snapshot

GET/api/token/:mint/lp-pull-candidatesDEVELOPER+

Scans the token's top 10 holders for candidate large liquidity withdrawals

/api/wallet-reputation/:address is deprecated — it still works and returns the same fields, plus "deprecated": true. Move to /api/wallet/:address/intelligence.

Rate Limits

Requests per UTC day. Same numbers api-auth.ts enforces server-side — not a second copy that could drift. Enqueueing a job and each poll both count as one request.

FREE100 requests/day
DEVELOPER5,000 requests/day
GROWTH50,000 requests/day
INTERNALUnlimited

Status & Error Codes

202Job accepted and queued — the response carries jobId and poll, not a result
400Malformed request — invalid JSON, a non-base58 address, or a batch over the tier cap
401Missing or invalid API key, and no dashboard session
403Key revoked, or this tier doesn't permit this endpoint (e.g. FREE calling a DEVELOPER+ route)
404Job or wallet not found — job polling is ownership-scoped, so another account's job id also reads as 404
429Daily rate limit exceeded for this key's tier
503Platform degraded — database unreachable (health check only)

A job that fails during processing is not an HTTP error: the poll route returns 200 with status: "failed" and an error message. Check the body, not just the code.

SDK

No official SDK exists yet. Every endpoint is plain REST + JSON — a fetch call or curl works without one. If you build a wrapper library, we'd genuinely like to hear about it.

Best Practices

  • Split snapshots larger than your tier cap into sequential batches — clustering runs per job, so keep wallets you expect to be related in the same batch
  • Poll every 3–5 seconds, not tighter; polls count against your daily limit
  • Persist the jobId. Results stay retrievable, so a dropped connection never means re-running the batch
  • Never auto-exclude on confidence alone — read evidence[] and check smartMoneyMembers first
  • /wallet/:address/token-risk and /token-risk/:mint answer different questions — a wallet's own exposure vs. a specific token's risk vectors — check you're calling the right one
  • Fund-flow and consolidation only label CEX/bridge destinations that are in the verified registry. An empty knownDestination means unlabeled, not unrelated