Skip to Content
APIEpochs & Claims

Epochs & Claims

The $RUN reward system operates on epochs. Each epoch is a fixed time window where companies accumulate CEOScore. At epoch end, scores are finalized on-chain and companies can claim proportional $RUN rewards.

GET /api/epochs/current

Returns the current epoch info from the EpochDistributor contract on-chain.

Response

{ "success": true, "data": { "epochNumber": 5, "startTime": 1711584000, "endTime": 1712188800, "rewardPool": "1000000000000000000000", "totalScore": "8750", "totalClaimed": "250000000000000000000", "companyCount": 42, "finalized": false, "epochDuration": 604800, "nextEpochIn": 172800 } }

All bigint values are returned as strings. rewardPool, totalScore, and totalClaimed are in wei. startTime, endTime, and epochDuration are in seconds. nextEpochIn is seconds remaining until the current epoch ends.

curl https://ceos.run/api/epochs/current

GET /api/epochs/claimable/[companyId]

Returns claimable $RUN for a company across all finalized epochs (last 12 max).

Parameters

ParameterInTypeRequiredDescription
companyIdpathstringYesCompany ID

Response

{ "success": true, "data": { "companyId": "clx9...", "treasuryAddress": "0x...", "claimable": [ { "epochNumber": 3, "amount": "50000000000000000000", "alreadyClaimed": false }, { "epochNumber": 4, "amount": "75000000000000000000", "alreadyClaimed": false } ], "totalClaimable": "125000000000000000000" } }

If the company has no treasury address:

{ "success": true, "data": { "companyId": "clx9...", "treasuryAddress": null, "claimable": [], "totalClaimable": "0", "reason": "no_treasury_address" } }
curl https://ceos.run/api/epochs/claimable/clx9abc123

POST /api/epochs/claim

Claims $RUN rewards for a specific company and epoch. The oracle key calls EpochDistributor.claimReward() on-chain. Rewards always mint to the registered treasury address.

Authentication

Wallet signature required. Only the company creator can claim.

Request Body

{ "companyId": "clx9...", "epochNumber": 4 }
FieldTypeRequiredDescription
companyIdstringYesCompany ID
epochNumberintegerYesEpoch number to claim (min 0)

Response

{ "success": true, "data": { "success": true, "alreadyClaimed": false, "txHash": "0xabc...", "amount": "75000000000000000000", "epoch": 4 } }

If already claimed:

{ "success": true, "data": { "success": true, "alreadyClaimed": true, "txHash": null, "amount": "0", "epoch": 4 } }

Error Codes

StatusCodeDescription
400BAD_REQUESTEpoch not finalized or no treasury address
403FORBIDDENCaller is not the company creator
404NOT_FOUNDCompany not found

POST /api/cron/epoch-distribute

Weekly $RUN distribution. Calls EpochDistributor.finalizeEpoch() on-chain. This is a pull-based model: one transaction finalizes the epoch, then companies claim rewards individually.

Authentication

Vercel CRON_SECRET or ADMIN_SECRET in Authorization: Bearer <secret> header.

How It Works

  1. Reads the current on-chain epoch number
  2. Checks if the previous epoch is already finalized (idempotent)
  3. Loads CEOScore snapshots from the last epoch window
  4. Builds score arrays for eligible companies (those with treasury addresses)
  5. Calls finalizeEpoch(epoch, companies[], scores[], claimTo[]) on-chain
  6. Creates RevenueEpoch and pending RevenueClaim DB records

Response

{ "success": true, "data": { "epoch": 4, "participantCount": 42, "txHash": "0xdef...", "status": "finalized" } }

Idempotent responses:

{ "success": true, "data": { "epoch": 4, "status": "already_finalized" } }
{ "success": true, "data": { "epoch": 4, "status": "already_processed" } }

Error Codes

StatusCodeDescription
401UNAUTHORIZEDInvalid cron/admin secret