API and data model

Every staking endpoint with its method, path and permission key, the six database tables and their columns, and the enums the whole product turns on.

2 min readUpdated 3 August 2026api, reference, permissions, schema

Everything on this page is generated from the shipped route handlers and models. Permission keys are enforced server-side; a frontend screen that hides a button is convenience, the key on the endpoint is the control.

User endpoints

All under /api/staking. Authenticated unless marked public.

Lists ACTIVE pools. Optional filters: token, minApr, maxApr, minLockPeriod.
One pool with aggregate stats and the caller's own positions in it.
Analytics for one pool over a timeframe.
The caller's positions, paginated. Filter by status or poolId.
Opens a position. Requires the invest_staking KYC feature. Rate limited to 5 per minute.
One position belonging to the caller.
Earning rows for one position. Filter by claimed status.
Claims every unclaimed earning row on a position. Requires withdraw_staking. Rate limited to 10 per hour.
Requests or performs an exit. Requires withdraw_staking. Behaviour depends on the lock state and the approval setting.
Reward calculator. Body takes amount and duration in days; returns a projection per eligible pool.
The caller's staking summary — total staked, earnings, unclaimed, active positions, and a per-token breakdown.
The caller's earning history. Filter by claimed status, pool and timeframe.
Public. Platform statistics: locked principal, active stakers, principal-weighted average APR.
Public. Landing page payload — promoted pools, short-lock pools, coming-soon pools and activity.

/api/staking/stats counts ACTIVE and PENDING_WITHDRAWAL principal only. A position that has asked to leave but not yet settled is still locked capital; one that has completed or been cancelled has already been paid back. Every staking surface uses this definition, so the landing page, the user dashboard and the admin console agree.

Admin endpoints

All under /api/admin/staking.

Dashboard and reporting

The solvency console: maturity ladder, withdrawal queue, per-pool APR comparison and the per-asset ledger. Accepts timeRange of 24h, 7d or 30d.
Staking analytics.
The staking admin activity log.

Pools

Paginated pool list for the data table.
All pools, unpaginated.
Creates a pool. Rejects a second ACTIVE pool for the same symbol and wallet type.
Bulk delete by IDs.
Bulk status change.
Sets the display order from an ordered list of IDs.
Alternative reorder form.
One pool.
Updates a pool. Strips availableToStake; accepts capacityDelta as a signed change.
Deletes a pool. Refused while it holds ACTIVE or PENDING_WITHDRAWAL positions.
Changes one pool's status.
Pool analytics.
Detailed pool performance history.
Positions in one pool, with computed rewards.

Positions

Paginated position list with computed rewards and earning detail.
All positions.
Updates admin notes, or transitions to COMPLETED, CANCELLED or back to ACTIVE. Rejects any attempt to set economic fields.
Bulk complete, cancel or withdraw, up to 100 positions, each in its own transaction.
Bulk status change.
Deletes a position. Refused while ACTIVE or PENDING_WITHDRAWAL.
Bulk delete by IDs.

Earnings and performance

Aggregated platform earnings.
Records a bookkeeping-only admin earning. Moves no money.
Marks an admin earning row as claimed. A bookkeeping flag, not a payment.
One-off BONUS payout of an explicit amount, split pro-rata. One per pool per cycle.
APR accrual catch-up. Credits only the outstanding delta. Supports dryRun and a positionIds subset of up to 100.
Lists external pool performance records.
Records external pool performance for a date. Future dates rejected.

/earning/distribute (singular) pays an explicit amount as a BONUS. /earnings/distribute (plural) runs the APR accrual engine and writes REGULAR rows. They are deliberately separate namespaces so they can never double-pay a period. Calling the wrong one is the most likely way to overpay a pool.

Tables

All six use UUID primary keys, timestamps and soft deletes.

staking_pools

Column Type Notes
name string(191) 2–100 characters
token · symbol string(50) · string(10) Symbol drives all wallet routing
icon · description string(191) · text
walletType enum FIAT · SPOT · ECO
walletChain string(191) Required for ECO
apr decimal(10,8) ≥ 0
lockPeriod integer ≥ 1 day
minStake · maxStake decimal(36,18) Max is nullable and must exceed min
availableToStake decimal(36,18) Live capacity
earlyWithdrawalFee · adminFeePercentage decimal(10,8) 0–100
status enum ACTIVE · INACTIVE · COMING_SOON
isPromoted · order boolean · integer Presentation
earningFrequency enum DAILY · WEEKLY · MONTHLY · END_OF_TERM
autoCompound boolean
externalPoolUrl · profitSource · fundAllocation · risks · rewards url · text x4 Disclosure copy, never read by logic

Positions are RESTRICT on delete; admin earnings and performance records cascade.

staking_positions

Column Type Notes
userId · poolId uuid
amount decimal(36,18) Must be > 0
startDate · endDate datetime Start must precede end
status enum ACTIVE · COMPLETED · CANCELLED · PENDING_WITHDRAWAL
withdrawalRequested · withdrawalRequestDate boolean · datetime The date prices an early exit
adminNotes · completedAt text · datetime completedAt only valid on COMPLETED
apr · adminFeePercentage · earlyWithdrawalFee decimal(16,8), nullable Terms snapshotted at stake time. Null on legacy rows falls back to the live pool.
lastDistributionDate datetime, nullable The accrual watermark. Null is treated as startDate.

staking_earning_records

Column Type Notes
positionId uuid
amount double ≥ 0
type enum REGULAR · BONUS · REFERRAL
description string(191) Truncated by writers to fit
isClaimed · claimedAt boolean · datetime
periodBucket string(100), nullable Distribution-cycle key

Unique on (positionId, type, periodBucket). NULL buckets on legacy rows never collide because MySQL treats NULLs as distinct. REGULAR rows written by the accrual engine carry accrual_YYYY-MM-DD; bonus distributions carry poolId:frequency:LABEL:cycle. Nothing currently writes REFERRAL.

staking_admin_earnings

Column Type Notes
poolId uuid
amount · currency double · string(10) Currency mirrors the pool symbol
isClaimed boolean Bookkeeping acknowledgement only
type enum PLATFORM_FEE · EARLY_WITHDRAWAL_FEE · PERFORMANCE_FEE · OTHER
periodBucket string(100), nullable Unique with (poolId, type)

staking_external_pool_performances

poolId, date (not in the future), apr, totalStaked, profit, notes. Reference data — no engine reads it.

staking_admin_activities

userId (null for cron-driven actions), action (create · update · delete · approve · reject · distribute), type (pool · position · earnings · settings · withdrawal) and relatedId.

Wallet and transaction types

Type Written when
STAKING Principal debited at stake time, and principal returned at settlement
STAKING_REWARD A user claims earnings

Idempotency keys used by the money paths: staking_create_<positionId> for the stake debit, staking_principal_return_<positionId> for the principal return (shared across every transition), and a hash of the claimed row IDs for a claim.

Permission keys

Key Guards
access.staking Admin overview, dashboard and analytics endpoints
access.staking.pool · view.staking.pool Pool screens and reads
create.staking.pool · edit.staking.pool · delete.staking.pool Pool writes
access.staking.position · view.staking.position Position screens and reads
create.staking.position · edit.staking.position · delete.staking.position Position writes, including withdrawal approval
access.staking.earning · view.staking.earning Earnings screen and reads
create.staking.earning · edit.staking.earning Both distribute endpoints, manual earnings, claiming
view.staking.performance · create.staking.performance External performance records
view.staking.activity Activity log
access.staking.settings Staking settings screen

Key derivation and the places a key must exist are covered in Permissions.