ALMERU Launch on X Connect
Docs · Build

REST API

Eight endpoints, JSON in and JSON out, over the same base URL the agent itself calls.

Base URL and versioning

All requests go to https://api.almeru.app/v1. The version lives in the path and is only incremented for breaking changes; additive fields can appear in any response, so parse defensively and ignore what you do not recognise. Every response carries an X-Request-Id header — keep it, it is the only thing worth quoting when something looks wrong.

Authentication

Reads are open and unauthenticated but rate-limited by IP. Writes require a bearer key with write scope:

Authorization: Bearer alm_live_7f2c9a4be1d84605
Content-Type: application/json

Keys are prefixed alm_live_ or alm_test_. A test key resolves against the same schemas but never reaches the chain: launches created with one always stop after simulation. Send keys in the header only — never in a query string, and never from a browser you do not control.

Endpoints

MethodPathAuthPurpose
GET/protocolsOpenList protocols, newest first.
GET/protocols/{slug}OpenOne protocol in full.
GET/minersOpenList miners with their current round.
GET/miners/{id}OpenOne miner by id or slug.
POST/launchesBearerCreate a launch.
GET/launches/{id}BearerStep-by-step launch state.
GET/wallets/{xUserId}OpenDerive and inspect an agent wallet.
GET/treasury/{address}OpenTreasury, reserve and staking snapshot.

GET /protocols

Query parameters: kind (treasury | miner), status (live | settling | retired), limit (1–100, default 25) and cursor.

curl -s "https://api.almeru.app/v1/protocols?kind=treasury&limit=2"
{
  "data": [
    {
      "slug": "harbor-reserve",
      "name": "Harbor Reserve",
      "ticker": "HRBR",
      "kind": "treasury",
      "status": "live",
      "chain_id": 4663,
      "address": "0x7b41c0a2e5d93f6a8c1b0e47d2f95a3c6b8e1d40",
      "created_at": "2026-05-02T09:14:31Z",
      "treasury_value": "184320.55",
      "backing_per_token": "1.0742"
    },
    {
      "slug": "tin-line",
      "name": "Tin Line",
      "ticker": "TINL",
      "kind": "treasury",
      "status": "live",
      "chain_id": 4663,
      "address": "0x91a7f60c8e34d2b5107aec9d3f8b64205cd1e7a9",
      "created_at": "2026-06-18T14:47:02Z",
      "treasury_value": "42780.10",
      "backing_per_token": "1.0113"
    }
  ],
  "has_more": true,
  "next_cursor": "c3Rhcl9oYXJib3ItcmVzZXJ2ZQ"
}

Pagination is forward-only with opaque cursors: pass next_cursor back as cursor and stop when has_more is false. Cursors are tied to the filter that produced them — changing kind mid-walk invalidates them.

GET /protocols/{slug}

curl -s https://api.almeru.app/v1/protocols/harbor-reserve
{
  "slug": "harbor-reserve",
  "name": "Harbor Reserve",
  "ticker": "HRBR",
  "kind": "treasury",
  "status": "live",
  "chain_id": 4663,
  "address": "0x7b41c0a2e5d93f6a8c1b0e47d2f95a3c6b8e1d40",
  "deployer": "0x2ea9c4b7f18d3056a9c7e21b40fd7c85ba36d0f1",
  "description": "A slow reserve for the north dock.",
  "image_url": "/media/harbor-reserve.png",
  "bootstrap_split": { "emissions_bps": 8000, "staking_bps": 1000, "reserve_bps": 1000 },
  "routing": { "operations_bps": 100, "losing_side_recycled_bps": 1000, "deployment_fee_bps": 100 },
  "created_at": "2026-05-02T09:14:31Z"
}

GET /miners and GET /miners/{id}

/miners accepts phase (open | lock | draw | settle), limit and cursor, and returns the same envelope as /protocols. The single-miner route accepts either the id or the slug.

curl -s https://api.almeru.app/v1/miners/copper-field
{
  "id": "mnr_8fq2k1x",
  "slug": "copper-field",
  "name": "Copper Field",
  "ticker": "CPFD",
  "chain_id": 4663,
  "tiles": 25,
  "round": {
    "index": 41207,
    "phase": "open",
    "epoch": 27714055,
    "opens_at": "2026-07-24T11:03:00Z",
    "locks_at": "2026-07-24T11:04:00Z",
    "winner_reward": "12.480000",
    "token_pot": "1.248000",
    "entries": 63,
    "entry_value": "418.75"
  },
  "odds": { "tile": "1/25", "token_pot_release": "1/333" },
  "schedule": { "funded_rounds_total": 525600, "funded_rounds_remaining": 484393 },
  "routing": { "operations_bps": 100, "losing_side_recycled_bps": 1000, "deployment_fee_bps": 100 }
}

POST /launches

The only endpoint that spends. Send Idempotency-Key: a retry with the same key returns the original launch rather than creating a second one, and keys are remembered for 24 hours.

curl -s -X POST https://api.almeru.app/v1/launches \
  -H "Authorization: Bearer $ALMERU_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: copper-field-2026-07-24-01" \
  -d '{
        "kind": "miner",
        "name": "Copper Field",
        "ticker": "CPFD",
        "bootstrap": { "asset": "USDC", "amount": "1200.00" },
        "x_user_id": "1596180932847104000"
      }'
{
  "id": "lnc_3v7pq0m",
  "state": "queued",
  "kind": "miner",
  "quote": {
    "bootstrap": "1200.00",
    "deployment_fee": "12.00",
    "total": "1212.00",
    "cap_remaining_24h": "12300.00"
  }
}

The same call from JavaScript:

const res = await fetch('https://api.almeru.app/v1/launches', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ' + process.env.ALMERU_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'copper-field-2026-07-24-01',
  },
  body: JSON.stringify({
    kind: 'miner',
    name: 'Copper Field',
    ticker: 'CPFD',
    bootstrap: { asset: 'USDC', amount: '1200.00' },
    x_user_id: '1596180932847104000',
    simulate_only: false,
  }),
});

if (!res.ok) {
  const { error } = await res.json();
  throw new Error(error.code + ' — ' + error.message);
}

const launch = await res.json();
console.log(launch.id, launch.state, launch.quote.total);

GET /launches/{id}

{
  "id": "lnc_3v7pq0m",
  "state": "settled",
  "kind": "miner",
  "created_at": "2026-07-24T11:02:41Z",
  "steps": [
    { "name": "parse",    "status": "ok", "at": "2026-07-24T11:02:41Z" },
    { "name": "validate", "status": "ok", "at": "2026-07-24T11:02:41Z" },
    { "name": "quote",    "status": "ok", "at": "2026-07-24T11:02:42Z" },
    { "name": "simulate", "status": "ok", "block": 19442806 },
    { "name": "execute",  "status": "ok", "block": 19442807,
      "tx": "0x4c1d9a70be25f3c8d1a6470b9e83f215cc47ab30" }
  ],
  "protocol": { "slug": "copper-field", "address": "0x5d38b71ea940c26f8b03d571ce4a92f0d7e19c02" }
}

States move queuedsimulatingexecutingsettled, or terminate at failed. Poll no faster than once per second; a settled launch is immutable, so cache it and stop polling.

GET /wallets/{xUserId} and GET /treasury/{address}

curl -s https://api.almeru.app/v1/wallets/1596180932847104000
{
  "x_user_id": "1596180932847104000",
  "address": "0x2ea9c4b7f18d3056a9c7e21b40fd7c85ba36d0f1",
  "chain_id": 4663,
  "deployed": true,
  "root_owner": "0x9c3f18ba7e0d54216fbc9037ad81e5c4207fb6d8",
  "agent_signer": { "granted": true, "method": "executeLaunch" },
  "caps": { "per_launch": "5000.00", "rolling_24h": "15000.00", "asset": "USDC" },
  "balance": "3184.90"
}

The wallet route answers for any account id, deployed or not — derivation is a pure function of the immutable id, so an undeployed wallet returns the same address with "deployed": false. /treasury/{address} returns treasury_value, supply, backing_per_token, a reserve object with the timelock, and a staking object carrying vesting_days: 7 and return_threshold_bps: 1000.

Status codes and errors

Errors are always a single JSON object under error, with a stable code, a human message, an optional details map and the request id.

{
  "error": {
    "code": "cap_exceeded",
    "message": "Bootstrap plus fee exceeds the rolling 24-hour cap.",
    "details": { "requested": "1212.00", "cap_remaining_24h": "840.00" },
    "request_id": "req_0b93f7c1a2"
  }
}
StatusCodesNotes
200Success. Reads and completed writes.
201Launch created.
400invalid_requestMalformed body or query parameter.
401unauthorizedMissing, malformed or revoked key.
403forbidden_scopeRead-only key, or no live authorization grant on the wallet.
404not_foundUnknown slug, id or address.
409ticker_taken, idempotency_conflictReused key with a different body.
422cap_exceeded, insufficient_funds, simulation_failedWell-formed but not executable. Nothing spent.
429rate_limitedSee Retry-After.
503upstream_unavailableChain or indexer degraded. Retry reads with backoff.

Rate limits

SurfaceLimitWindow
Unauthenticated reads60 requestsper minute per IP
Authenticated reads600 requestsper minute per key
POST /launches10 requestsper minute per key
POST /launchesRolling 24-hour spend capper wallet, enforced on chain

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. On a 429, honour Retry-After and back off exponentially with jitter. The per-minute limit is a service courtesy; the rolling 24-hour cap is a contract rule and no amount of backoff moves it.

Prefer types over handwritten fetches? The SDK wraps every endpoint on this page, converts snake_case to camelCase, and turns each error code into a typed exception.

Keep reading