Each game on Arena54 can be its own small economy with its own currency. Your server reports a few figures, and Arena54 works out what one unit of your currency is worth in every other reporting game’s. The rates are public at arena54.com/rates. This is game money: no rate here is a price for real money. Rates have their own switch on Arena54’s side; while it is off these calls answer 503 module_off.
Method
Path
Purpose
POST
/v1/economy/report
Report your game’s economy: supply, minted, burned, four basket prices, output, active players. It moves your currency’s rates
GET
/v1/economy/rates
Every pair of listed currencies, both ways: the rate, where it came from and its change in a day
await fetch("https://developers.arena54.com/api/v1/economy/report", {
method: "POST",
headers: { Authorization: "Bearer " + process.env.ARENA54_SECRET_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
supply: 100000, minted: 500, burned: 200,
basket: { meal: 50, ride: 20, wage: 400, rent: 3000 },
output: 9000, active_players: 340,
policy_rate: 5, // optional: your own interest rate, in percent
naira_per_unit: 0.5, // optional: your own selling price for one unit
}),
});
// → { stored: true, report, at, counted: true, held: null }
Amounts are whole numbers in your own currency.
The basket is what four things cost in your game: a meal, a ride, a day’s wage and a month’s rent. Use the nearest thing your game has, and keep using the same one, or your prices will seem to jump.
supply is all the currency that exists now. minted, burned and output (what players earned by playing) are since your last report.
Once an hour is plenty. A report sooner than the minimum gap answers 429 too_soon.
A report whose prices move too far since your last one is stored and held (counted: false, with the reason in held) until a person at Arena54 has looked at it. It moves no rate while held.
A test key checks the shape and stores nothing.
Each report writes an entry in your game’s audit log.
Reading rates
GET /v1/economy/rates returns every pair both ways. source is worked when the rate comes from the two economies’ figures, and market when enough swaps were made on the pair in a day for their price to be used. GET /v1/economy/currencies returns each currency’s figures. Either works with any key of a registered game.
How a rate is worked out
Buying power: if the basket costs 200 in your game and 500 in another, one of yours is worth 2.5 of theirs.
A pull from each economy: faster growth in output, and taking in more than you send out through the exchange, make a currency a little stronger. Each pull is capped.
The market: when players make enough swaps on a pair in a day, the price of those swaps is the rate.
A point is added to a pair’s chart only when a report or swaps changed the rate. Nothing is invented between reports.
Limits and refusals
Status
Code
When
429
too_soon
An economy report arrived sooner after the last one than is allowed. Retry-After says how long to wait.
503
module_off
Arena54 has switched the module off for now.
403
secret_key_required
The call needs the game’s secret key, from a server.
Build it with AI
Paste one of these into Claude Code, Codex or another coding agent, inside your project. It carries the real contract, so the agent does not have to guess.
Add Arena54 currency rates
# Add Arena54 currency rates to this project
Goal: Report this game’s economy to Arena54 so its currency has a rate against other games’, and show those rates.
You are a coding agent working inside my project. Do the work. Do not ask me anything you can find out by reading the project. Do not invent an endpoint, a field or an error code that is not written below.
## 1. Look at the project first
- Find the framework and language, how the project is run and tested, and where the server knows how much currency exists, what was created and destroyed, what things cost, and how many players were active.
- Find how the project reads configuration and environment variables, and which files git ignores.
- Any backend or script will do. Use the HTTP client and the router the project already has, or the runtime’s own if it has none. If the project has nowhere to run code off the page, stop and tell me.
- If the project has no environment example file, add one, and load the environment the way the runtime provides.
- If the project already has an Arena54 client or key variable, reuse it.
- Tell me in two or three lines what you found and what you will change, then carry on.
## 2. The contract
Base URL: `https://developers.arena54.com/api/v1` (called $BASE below).
Every answer is JSON in one envelope:
```json
{ "success": true, "data": …, "meta": { "request_id": "req_…", "mode": "test" } }
{ "success": false, "error": { "code": "…", "message": "…" }, "meta": { "request_id": "req_…", "mode": "test" } }
```
Branch on `error.code`. Log `meta.request_id` with any failure.
Keys and headers:
- Use the game’s SECRET key (it starts `a54_test_` or `a54_live_`, with no `pub_`). Read it from the environment variable `ARENA54_SECRET_KEY`, on the server only. Never send it to a page and never commit it. A secret key that arrives from a browser is refused.
- Send `Authorization: Bearer <secret key>`. A body is JSON; a `Content-Type` header is optional.
- A test key writes to test data of its own that never touches live data. Build with the test key.
- A 429 carries `Retry-After` in seconds.
Calls:
- POST /economy/report: Report your game’s economy: supply, minted, burned, four basket prices, output, active players. It moves your currency’s rates.
data: { "stored": true, "report": 12, "at": "…", "counted": true, "held": null } (a test key answers { "stored": false, "test": true }; "held" is a sentence when a price jumped too far and the report waits for a person)
- GET /economy/rates: Every pair of listed currencies, both ways: the rate, where it came from and its change in a day.
data: [{ "from": "game-a", "to": "game-b", "currency_from": "coins", "currency_to": "cowries", "rate": 2.5, "source": "worked" | "market", "at": "…", "day_change_pct": 1.2 | null }]
- GET /economy/currencies: Each listed currency’s figures: prices, output, supply, trade balance, policy rate.
data: [{ "slug": "game-a", "name": "…", "currency": "coins", "reported_at": "…", "late": false, "supply": 100000, "minted": 500, "burned": 200, "output": 9000, "players": 340, "basket": { "meal": 50, "ride": 20, "wage": 400, "rent": 3000, "cost": 868 }, "inflation_pct": 1.4 | null, "growth_pct": 3 | null, "money_growth_pct": 0.3 | null, "trade_balance": -120, "policy_rate_pct": 5 | null, "naira_per_unit": 0.5 | null }]
How it works:
- All amounts are whole numbers in the game’s own currency.
- `basket` is the price, in the game, of the same four things every game prices: a meal, a ride, a day’s wage, a month’s rent. Use the nearest thing the game has and keep using the same one.
- `supply` is all the currency that exists now. `minted` and `burned` are what was created and destroyed since the last report. `output` is what players earned by playing since the last report.
- `policy_rate` is the game’s own interest rate in percent, if it has one. `naira_per_unit` is the game’s own selling price for one unit, if it sells its currency. Leave either out if it does not apply.
- Arena54 works the rates out. Never send a rate.
- A report whose prices jump too far since the last one is stored but held (`counted: false`) until a person at Arena54 looks at it.
- With a TEST key the report is checked and not stored.
Refusals:
- 429 too_soon: An economy report arrived sooner after the last one than is allowed. Retry-After says how long to wait.
- 401 missing_key: No Authorization header, or what follows “Bearer” is not shaped like a game’s key.
- 401 invalid_key: The key is shaped like one but is unknown or revoked, or its game is gone.
- 503 module_off: Arena54 has switched the module off for now.
- 403 secret_key_required: The call needs the game’s secret key, from a server.
- 429 rate_limited: Too many calls this minute, for the key or for one player. Retry-After says how long to wait.
## 3. Do this, in order
1. Add ARENA54_SECRET_KEY to the project’s environment example file with an empty value. Read it only in server code.
2. Write one server function `economyNow()` that gathers the figures from where the project keeps them. If a figure does not exist in this project, stop and ask me where it comes from. Do not make one up.
3. Write `reportEconomy()` that calls POST /economy/report with those figures.
4. Call `reportEconomy()` on the schedule the project already has (a cron route, a worker). Once an hour is enough. If the project has no scheduler, stop and tell me.
5. On 429 `too_soon`, skip this run. Do not retry in a loop.
6. If I ask for rates to be shown: read GET /economy/rates on the server, cache the answer for five minutes, and show each pair’s `rate` with `source`. Say “worked out” for `worked` and “from swaps” for `market`.
## 4. Prove it works
Set `BASE` and `ARENA54_SECRET_KEY` (my TEST secret key) in the shell. If I have not given you a key, ask me for a TEST secret key once, and for nothing else. `BASE=https://developers.arena54.com/api/v1`.
- Run: `curl -X POST "$BASE/economy/report" -H "Authorization: Bearer $ARENA54_SECRET_KEY" -H "Content-Type: application/json" -d '{"supply":100000,"minted":0,"burned":0,"basket":{"meal":50,"ride":20,"wage":400,"rent":3000},"output":0,"active_players":1}'`. Expect HTTP 200 with `"stored": false, "test": true`.
- Run it with `"meal": 0`. Expect HTTP 400 `bad_request`.
- Run: `curl "$BASE/economy/rates" -H "Authorization: Bearer $ARENA54_SECRET_KEY"`. Expect HTTP 200 and a list, which may be empty.
- Run the project’s own `reportEconomy()` once with the test key and show me the figures it sent.
- A 503 `module_off` means Arena54 has not opened rates yet. Tell me, and leave the code in place.
Show me the output of each check.
## 5. Rules you must not break
- Report what is true. A figure made up to move a rate is the one thing this must never do.
- The secret key never reaches a page, a game build or a commit.
- Never present a rate as a price at which anyone will buy or sell real money.
## 6. Done when
- The report is sent on a schedule from the server.
- The test report answered `stored: false, test: true`.
- Every figure sent comes from the project’s own data, and you have told me where each one comes from.
Then give me a short list of the files you changed and anything I must do by hand.
For a person, not needed for this task: https://developers.arena54.com/docs · This prompt: https://developers.arena54.com/prompts/economy.server.md