Skip to content

Frontend Integration Guide

The single onboarding entry point for frontend developers new to this project. For backend operations see backend/RUNBOOK.md; for module internals see backend/README.md.

Relative links are resolved against the doc/ directory.


1. The whole picture in one page

Pump Party is a game where users create clones (AI trader personas), validate them through a 6-hour audition, and later enter them into races. The v1 backend has the paid audition path and prop-account runtime mostly in place; race operations are the main missing product vertical.

User (wallet → backend)
   │  SIWE login → JWT issued

[Clone Service]  ── clone identity, strategy graph, entitlement

   │ pay audition fee (SignedVault on MegaETH)

[Economy]        ── deposit confirmation/sync → entitlement grant

   │ start the audition

[Lifecycle]      ── 6h audition window → evaluator (pass / fail)

[Prop Trading]   ── synthetic account ($100k fake USDC), positions,
                    safety gates, paper fills

   │ AI trader proposes actions → safety gates reject policy violations →
   │ surviving actions are filled at the latest Hyperliquid mid price,
   │ updating the synthetic account's cash + positions

[AI Trading]     ── model calls, prompt v1, artifact storage

[Public Profile] ── publishes immediately on submit. Anyone can report;
                    admins triage reports and can retire profiles.

Each module is its own directory inside the backend (backend/src/modules/{user,clone,ai-trading,prop-trading,economy,lifecycle,data-ingestion}/) and modules talk to each other only through service interfaces. From a frontend perspective it's all one HTTP API.


2. Quickstart

For the current local app commands, frontend env, optional workers, and VitePress docs server, start with Development Quick Start.

bash
# Backend setup is in backend/RUNBOOK.md
curl http://127.0.0.1:8787/api/v1/assets   # 401 expected (auth required)

Smoke the documented frontend flow against a running local backend:

bash
cd backend
npm run api

# in another terminal
npm run test:smoke:frontend-flow

The smoke runner uses real SIWE auth, then walks through clone creation, strategy draft/publish, audition-fee quote, public profile, reporting, and audition submission. If local SignedVault env or a real deposit nonce is unavailable, it seeds the audition entitlement through local service auth so the lifecycle route can still be exercised. Set PUMP_PARTY_SMOKE_DEPOSIT_NONCE=<nonce> to test the real /economy/audition-fee/submit step. If the local API has PUMP_PARTY_BACKEND_SERVICE_TOKEN configured, expose the same value to the smoke runner or set PUMP_PARTY_SMOKE_SERVICE_TOKEN.

Get a JWT (or use the dev shortcut from §5):

bash
# 1) ask for a challenge
curl -X POST http://127.0.0.1:8787/api/v1/auth/siwe/challenge \
  -H 'content-type: application/json' \
  -d '{"address":"0x...","chainId":4326,"domain":"localhost:3000","uri":"http://localhost:3000"}'

# 2) wallet signs the returned `message`
curl -X POST http://127.0.0.1:8787/api/v1/auth/siwe/verify \
  -H 'content-type: application/json' \
  -d '{"message":"...","signature":"0x..."}'

# response: { accessToken, refreshToken, user, ... }

Every subsequent call:

Authorization: Bearer <accessToken>

3. Domain glossary

The full vocabulary lives at domain-language. The terms you'll see in conversation:

TermOne-line definition
CloneA user-created AI trader persona. Operational identity (owner, model, strategy) and public identity (display name / bio) are split.
Strategy graph / draftThe data-block graph that defines the clone's behaviour.
EntitlementThe model strength + concurrent asset count a clone is allowed to use. Granted by paying an audition_fee.
AuditionA 6-hour qualifying round the clone must clear (return target + drawdown cap). If it fails, the user pays again and retries.
Voided auditionA round the admin invalidates because of infrastructure failure / data outage / post-hoc moderation strike. Refunds are out of scope for v1.
Prop accountThe synthetic (fake) trading account lifecycle creates. Starts with fake $100,000 USDC. No real assets. Every fill / position lives inside this account; nothing reaches a real exchange.
PositionA long/short the synthetic account is holding. Mark-to-market keeps unrealized PnL fresh.
Safety gatesEleven deterministic checks that drop AI actions violating policy (allowedSymbols, maxLeverage, available balance, position caps, etc.). Surviving actions go to the filler.
Paper fillThe simulator does not place real exchange orders. It assumes the action filled at the latest mid price and updates the synthetic account's cash + positions. Pretend money, pretend fills.
Decision run / actionOne AI trader cycle. If a proposed action survives the safety gates a paper fill follows.
SignedVaultThe MegaETH ETH escrow contract. Deposits are verified on-chain; withdrawals are authorised via EIP-712 signatures.
Mark-to-marketReprices every open position from the latest mid. Auto-liquidates if drawdown breaches 50%.

4. Authentication (SIWE on MegaETH)

Supported chains: mainnet 4326, testnet 6343 (controlled by AUTH_ALLOWED_CHAIN_IDS). Both EOA wallets and Porto Relay smart wallets are supported.

The frontend may obtain the signer from an injected wallet, WalletConnect/Reown, Porto, or a Privy embedded wallet. In all cases, Pump Party auth is the SIWE message/signature verified by the backend. Do not use Privy JWTs or privy-token as Pump Party app auth.

Flow

[frontend]                                        [backend]
   │                                                  │
   │ 1) POST /auth/siwe/challenge                    │
   │   { address, chainId, domain, uri }             │
   │ ─────────────────────────────────────────────► │
   │                                                  │
   │   { nonce, message, expiresAt, ... }            │
   │ ◄───────────────────────────────────────────── │
   │                                                  │
   │ 2) wallet signs `message`                       │
   │    injected / WalletConnect / Porto / Privy     │
   │                                                  │
   │ 3) POST /auth/siwe/verify                        │
   │   { message, signature }                         │
   │ ─────────────────────────────────────────────► │
   │                                                  │
   │   { accessToken, refreshToken, accessExpiresAt,  │
   │     refreshExpiresAt, user, isNewUser }          │
   │ ◄───────────────────────────────────────────── │

message already contains the nonce/domain/chainId; just feed the signed result straight into verify.

After verify, use the returned Pump Party accessToken as the bearer token for product APIs. Store and rotate only the Pump Party refreshToken; Privy user ids and tokens are not canonical user identity.

Token lifetimes

  • Access JWT: 24 hours by default (AUTH_ACCESS_TTL_SECONDS).
  • Refresh token: 14 days by default (AUTH_REFRESH_TTL_SECONDS).
  • Refresh tokens rotate: using the same refresh token twice triggers reuse-detection and revokes the whole session. Always store only the most recently issued refresh token.

Auth endpoints

POST   /api/v1/auth/siwe/challenge   → { message, nonce, ... }
POST   /api/v1/auth/siwe/verify      → { accessToken, refreshToken, user, ... }
POST   /api/v1/auth/session/refresh  { refreshToken } → fresh token pair
DELETE /api/v1/auth/session          (Bearer)  revoke session (logout)
GET    /api/v1/me                    (Bearer)  current user + primary wallet

Full spec: User Service Auth.


5. Dev-mode shortcut (development only)

Set AUTH_DEV_FALLBACK=true in .env and you can authenticate with a single header instead of going through SIWE:

x-user-id: dev-user-1

Production ignores this regardless of value. It exists so styling / layout work doesn't get blocked on signing flows.


6. API surface catalogue

Grouped by who is allowed to call. Bearer = SIWE-logged-in users. Service token (x-service-token) = backend-to-backend calls and admin tooling.

6.1 User-facing (Bearer)

MethodPathPurpose
GET/api/v1/meCurrent user + primary wallet
GET/api/v1/clone/clonesList clones owned by the authenticated user
GET/api/v1/clone/clones/:cloneIdClone metadata
PATCH/api/v1/clone/clones/:cloneIdUpdate clone name / model / etc.
POST/api/v1/clone/clones/:cloneId/public-profileOwner submit/edit public profile (name, username, description, square image URL)
GET/api/v1/clone/clones/:cloneId/public-profileOwner-facing public profile read
GET/api/v1/clone/public-profilesList public profiles owned by the authenticated user
GET/api/v1/clone/clones/:cloneId/entitlementCurrent entitlement
GET/PUT/api/v1/clone/clones/:cloneId/strategy/draftWork-in-progress strategy graph
POST/api/v1/clone/clones/:cloneId/decision-backtestStart an async backtest
GET/api/v1/clone/clones/:cloneId/decision-backtest/jobs/:jobIdPoll a backtest
POST/api/v1/clone/clones/:cloneId/strategy/publishPublish a new strategy version
GET/api/v1/clone/clones/:cloneId/strategy/versionsList strategy versions
GET/api/v1/clone/clones/:cloneId/strategy/versions/:strategyVersionIdOne version
POST/api/v1/economy/audition-fee/quotePricing quote ({ tier, assetCapacity, modelStrength })
POST/api/v1/economy/audition-fee/submitConfirm/sync the authenticated wallet's deposit, optionally create a paid clone, and return the entitlement
GET/api/v1/assetsTradeable assets
GET/api/v1/polymarket/markets/search?...Polymarket search
GET/api/v1/defillama/subjects/search?...DefiLlama search
GET/api/v1/ingestion/healthData ingestion health
GET/api/v1/ingestion/dashboardUser-facing ingestion dashboard data

6.2 Public read (no auth)

MethodPathPurpose
GET/api/v1/public/clones/:cloneId/profilePublicly visible clone profile. Returns 404 if the profile has been retired.

6.3 User reports (Bearer)

MethodPathPurpose
POST/api/v1/public/clones/:cloneId/reportAny user reports a public clone ({ category, detail? }). Self-reports are refused.

6.4 Backend / admin (Service token)

These are called by ops tooling or by other backend services. The frontend does not call these directly. If the frontend needs an admin UI, route it through a BFF.

Prop trading:

POST /api/v1/prop/accounts                                  # create
GET  /api/v1/prop/accounts/:propAccountId
GET  /api/v1/prop/clones/:cloneId/accounts
POST /api/v1/prop/accounts/:propAccountId/{start,complete,liquidate}
GET  /api/v1/prop/accounts/:propAccountId/{ledger,snapshot,orders,fills,positions}
GET  /api/v1/prop/accounts/:propAccountId/decision-context
POST /api/v1/prop/accounts/:propAccountId/decision-actions
GET  /api/v1/prop/decision-actions/:propActionId
POST /api/v1/prop/accounts/:propAccountId/mark-to-market
POST /api/v1/prop/mark-to-market-all

Audition lifecycle:

POST /api/v1/lifecycle/auditions                            # submit an audition
GET  /api/v1/lifecycle/auditions/:auditionRunId
GET  /api/v1/lifecycle/clones/:cloneId/auditions
POST /api/v1/lifecycle/auditions/:auditionRunId/evaluate    { force? }
POST /api/v1/lifecycle/auditions/evaluate-due               # cadence hook
POST /api/v1/lifecycle/auditions/:auditionRunId/void        # admin: void a round ({ adminUserId, reason })

Public profile + admin moderation (report-based):

GET  /api/v1/admin/moderation/reports?status=open|resolved|dismissed
GET  /api/v1/admin/moderation/clones/:cloneId/reports
POST /api/v1/admin/moderation/reports/:reportId/resolve     # { resolutionAction: retired|dismissed|no_action, ... }
POST /api/v1/admin/moderation/clones/:cloneId/retire        # direct retire ({ adminUserId, reason })
POST /api/v1/admin/moderation/clones/:cloneId/reinstate
GET  /api/v1/admin/moderation/clones/:cloneId/history

Prop admin (incident response):

POST /api/v1/admin/prop/accounts/:propAccountId/{pause,resume,lock,liquidate,recompute}
POST /api/v1/admin/prop/accounts/:propAccountId/{void-order,void-fill}
GET  /api/v1/admin/prop/accounts/:propAccountId/audit-log

Internal monitoring:

GET  /api/v1/internal/health/workers   # worker heartbeat summary

7. End-to-end scenarios

7.1 First-time signup

1. Connect wallet                              → wagmi/viem
2. POST /auth/siwe/challenge                   → message
3. Wallet signs the message
4. POST /auth/siwe/verify                      → accessToken, refreshToken
5. Persist tokens in localStorage / secure storage
6. GET /me                                     → user.id, primaryWallet
7. GET /clone/clones                           → load existing clones
8. POST /economy/audition-fee/quote            → quote paid clone creation
9. Wallet deposits ETH to SignedVault
10. POST /economy/audition-fee/submit { createClone, depositNonce, ... }
                                                   # v1 confirmation key; future intent flow can hide the nonce

Refresh both tokens just before the access token expires (e.g. 60s before) by calling POST /auth/session/refresh. Replace the stored pair with the new one.

7.2 Build a clone and run an audition

1. POST /economy/audition-fee/quote                        # pricing
2. Wallet deposits ETH to SignedVault (on-chain step)
3. POST /economy/audition-fee/submit { createClone, depositNonce, ... }
                                                            # confirm deposit, create clone, return entitlement
4. PUT  /clone/clones/:cloneId/strategy/draft              # write the graph
5. POST /clone/clones/:cloneId/decision-backtest           # run a sampled AI backtest
6. POST /lifecycle/auditions { cloneId, allowedSymbols, ... }
7. (wait 6 hours — the worker auto-evaluates)
8. GET /lifecycle/auditions/:id                            # read the result

Audition failed (evaluator below target):

11. The user fixes the strategy and submits a new audition fee.
    POST /economy/audition-fee/submit + POST /lifecycle/auditions { previousAuditionRunId }

Operationally voided round:

11. POST /lifecycle/auditions/:id/void   { adminUserId, reason }

Public profile — instant publish, report-based moderation:

11. POST /clone/clones/:cloneId/public-profile             # auto-approved → live at /public/...
12. (another user reports it) POST /public/clones/:cloneId/report
13. (admin reviews queue) GET /admin/moderation/reports?status=open
14. (admin decides) POST /admin/moderation/reports/:id/resolve
    { resolutionAction: 'retired' | 'dismissed' | 'no_action', ... }

Lifecycle owner routes are now frontend-safe backend user-auth routes. Evaluator and void routes remain internal service-token routes and should not be called directly from user UI.

7.3 Live clone status

To monitor an in-flight clone:

GET /api/v1/prop/accounts/:propAccountId/snapshot     → equity, drawdown, returnPct
GET /api/v1/prop/accounts/:propAccountId/positions    → open positions
GET /api/v1/prop/accounts/:propAccountId/fills        → recent fills
GET /api/v1/prop/accounts/:propAccountId/ledger       → every event (decision/fill/mark/...)

Suggested polling intervals:

  • snapshot: 5–10s
  • ledger: 30s+ if you only need new-action notifications

(There is no WebSocket / SSE in v1. Build polling or wrap a server-side SSE adapter if needed.)

7.4 E2E smoke test coverage

npm run test:smoke:frontend-flow is the local API smoke test for the frontend-critical route contract. It should run against an already-running backend and answer one question: can a new wallet complete the main backend flow the frontend depends on?

Covered path:

StepRoutesAssertion
Auth gateGET /api/v1/assets without authProtected route returns 401 unauthorized.
Wallet loginPOST /auth/siwe/challenge, POST /auth/siwe/verify, GET /meReal SIWE signature returns Pump Party tokens and a primary wallet.
Clone setupGET /clone/clones, POST /economy/audition-fee/submit { createClone, depositNonce, ... }, GET /clone/clones/:cloneIdA new user starts with no clones; Economy creates the clone only after the authenticated wallet deposit is confirmed.
Strategy loopGET/PUT /strategy/draft, POST /decision-backtest, POST /strategy/publishDefault graph can be edited, backtested with the standard 100k USDC account, and published.
Payment gatePOST /economy/audition-fee/quoteQuote succeeds when SignedVault env is configured, or returns the expected local economy_unavailable error.
Entitlement bridgePOST /economy/audition-fee/submitWith PUMP_PARTY_SMOKE_DEPOSIT_NONCE, the real deposit confirmation path creates the clone and returns entitlement. There is no direct entitlement-write HTTP fallback.
Public identityPOST/GET /clone/clones/:cloneId/public-profile, GET /public/clones/:cloneId/profileOwner publishes a profile and public read returns it.
ReportingPOST /public/clones/:cloneId/reportA second SIWE user can report the public clone.
Audition startPOST /lifecycle/auditions, GET /lifecycle/auditions/:id, GET /lifecycle/clones/:cloneId/auditionsOwner starts an audition, receives a running prop account, and can read/list the run.

Deliberately not covered:

  • Real on-chain deposit submission unless PUMP_PARTY_SMOKE_DEPOSIT_NONCE is supplied for a deposit that matches the local SignedVault config. Without that nonce, paid clone creation is skipped instead of using a direct clone or entitlement write route.
  • 6-hour audition evaluation, Arena Entry promotion, and race settlement. Those are worker/service-token flows, not frontend-critical first-run routes.
  • Live model-provider behavior. The smoke test is expected to work with the default noop provider so local runs are deterministic.

8. Response and error conventions

Success

Most endpoints return 200 OK with a single JSON object. Routes that create return 201 Created; idempotent re-submits return 200 and set a flag like alreadyExisted: true.

Errors

json
{
  "error": {
    "code": "invalid_transition",
    "message": "Audition aud_xxx is in terminal status passed; cannot void"
  }
}
StatusMeaning
400Bad input (invalid_*)
401Auth missing / expired / invalid
402Payment required (no_audition_entitlement)
403Authenticated but not authorised (forbidden)
404Resource not found
405Method not allowed
409State conflict (already_*, concurrent_*, invalid_transition)
422Business-rule violation
503A dependency is not configured

code is the stable identifier. The frontend maps codes to user-facing copy; the backend message is for operators (English, terse).

Codes worth knowing:

  • concurrent_submit / concurrent_create — two identical requests raced. Retry shortly.
  • idempotency_key_*_mismatch — same idempotency key was used with a different cloneId/owner. Use a fresh key.

9. Environments

EnvironmentAPI baseChainVault
local devhttp://127.0.0.1:8787testnet 6343(after env setup)
staging(deploy URL)testnet 6343testnet vault
production(deploy URL)mainnet 4326mainnet vault

The SIWE domain field must match the wallet-facing host exactly (localhost:3000, pumpparty.fun, etc.).


10. Reference docs

TopicLink
Domain vocabularydomain-language
Clash of Clones product specclash-of-clones-product-spec
Backend module layoutbackend/README.md
Backend operations (how to run it)backend/RUNBOOK.md
Prop trading module contractProp Trading Module
Clone Service detailsClone Service
Entitlements behaviorClone Service Module and Capacity And Model Tiers
All backend data contractsbackend-contract
Implementation roadmap / silo progressimplementation-roadmap

11. What v1 deliberately does not include

  • Race / tickets / settlement UI — held until race operations begin. Arena Entry now has backend read/sync/admin routes, but normal users still do not manage Arena slots directly.
  • Personal wallet (user-level asset management) — 0%, decided alongside race.
  • WebSocket / SSE — polling only.
  • A user-facing clone-creation endpoint — handled by an admin / internal tool today; if a self-service flow is needed it requires backend work.
  • Payment methods beyond ETH — no ERC20 / stablecoin support yet.