API Reference
Base URL: https://hoodly.fun/api/v1. All endpoints accept and return JSON. Authenticated endpoints require a robot API key as a bearer token.
POST /tasks — claim a task
Registers the intent to execute a task. Call this before the robot starts working, so the claim precedes the proof.
| Field | Type | Description |
|---|---|---|
| title | string, required | Short task title, max 200 characters |
| description | string | Optional free-form description |
| expected_result | object | Optional structured expectation, stored alongside the claim |
curl -X POST https://hoodly.fun/api/v1/tasks \
-H "Authorization: Bearer hdly_your_api_key" \
-H "Content-Type: application/json" \
-d '{"title": "Pick order #4821", "expected_result": {"items": 3}}'{
"task": {
"id": "8f14e45f-ea3c-4c1a-9b6e-2f7c0a1d3e5b",
"title": "Pick order #4821",
"description": null,
"status": "claimed",
"created_at": "2026-07-17T14:30:02.000Z"
},
"next": "Submit the execution proof to /api/v1/tasks/8f14e45f-.../complete when done."
}POST /tasks/:id/complete — submit the proof
Submits the execution evidence and anchors its hash on Robinhood Chain. This call waits for the transaction to be included, so expect a few seconds of latency.
| Field | Type | Description |
|---|---|---|
| proof | object, required | The execution evidence: telemetry, results, sensor readings — any JSON structure |
{
"verification": {
"task_id": "8f14e45f-...",
"robot_id": "c3d2e1f0-...",
"status": "verified",
"proof_hash": "0x9c22ff5f...",
"tx_hash": "0x4a8b17e2...",
"block_number": "1284930",
"anchored_at": "2026-07-17T14:32:40.000Z",
"explorer_url": "https://robinhoodchain.blockscout.com/tx/0x4a8b17e2..."
}
}A task can be completed once. A second attempt on a finished task returns 409 already_completed. A second attempt while the first one is still anchoring returns 409 completion_in_progress — do not retry immediately, poll GET /tasks/:id instead. Only one completion per task can ever reach the chain, so a retry storm cannot double-charge your credit or produce two anchors.
If the anchor transaction is submitted but not confirmed within the timeout, the response is 202 anchor_unconfirmed with the tx_hash. This is not a failure: the transaction may still land. Poll GET /tasks/:id or check the explorer before retrying — the reservation is deliberately not refunded, because the anchor may already exist on-chain.
{
"error": "anchor_unconfirmed",
"message": "The anchor transaction was submitted but not confirmed in time. It may still land — poll GET /api/v1/tasks/:id before retrying.",
"proof_hash": "0x9c22ff5f...",
"tx_hash": "0x4a8b17e2...",
"explorer_url": "https://robinhoodchain.blockscout.com/tx/0x4a8b17e2..."
}GET /tasks/:id — task status
Returns the task including the verification receipt once anchored.
curl https://hoodly.fun/api/v1/tasks/8f14e45f-... \ -H "Authorization: Bearer hdly_your_api_key"
GET /verify/:proofHash — public verification
No authentication required. Looks up the proof hash, independently re-reads the anchor transaction from Robinhood Chain and reports whether the on-chain record matches.
curl https://hoodly.fun/api/v1/verify/0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658
{
"verified": true,
"proof_hash": "0x9c22ff5f...",
"task": {
"id": "8f14e45f-...",
"title": "Pick order #4821",
"robot": "warehouse-arm-01",
"status": "verified"
},
"anchor": {
"tx_hash": "0x4a8b17e2...",
"block_number": "1284930",
"anchored_at": "2026-07-17T14:32:40.000Z",
"explorer_url": "https://robinhoodchain.blockscout.com/tx/0x4a8b17e2...",
"on_chain_payload_found": true
}
}POST /telemetry — ops snapshots
Records periodic robot health for the dashboard Statistics tab. Telemetry is not anchored on-chain — use task proofs for verifiable execution evidence. Send a single sample or a samples array (max 50).
| Field | Type | Description |
|---|---|---|
| battery_pct | number | null | Battery charge 0–100, optional |
| state | string | null | Convention: idle, busy, error, charging, offline (max 64 chars) |
| metrics | object | Arbitrary sensor readings, e.g. {"temp_c": 42, "cpu": 0.4} |
| recorded_at | string (ISO-8601) | Optional sample time; defaults to server now |
| samples | array | Optional batch of the fields above (max 50) |
curl -X POST https://hoodly.fun/api/v1/telemetry \
-H "Authorization: Bearer hdly_your_api_key" \
-H "Content-Type: application/json" \
-d '{"battery_pct": 87.5, "state": "busy", "metrics": {"temp_c": 41.2}}'{
"accepted": 1,
"samples": [
{
"id": "a1b2c3d4-...",
"recorded_at": "2026-07-30T05:12:00.000Z",
"battery_pct": 87.5,
"state": "busy",
"metrics": { "temp_c": 41.2 }
}
]
}POST /events — ops events
Logs a discrete ops event (errors, state changes, notices) for the Statistics tab. Not anchored on-chain.
| Field | Type | Description |
|---|---|---|
| event_type | string, required | e.g. error, state_change, info (max 64 chars) |
| severity | string | info (default), warn, or error |
| message | string | Human-readable detail, max 2000 characters |
| payload | object | Optional structured context |
curl -X POST https://hoodly.fun/api/v1/events \
-H "Authorization: Bearer hdly_your_api_key" \
-H "Content-Type: application/json" \
-d '{"event_type": "error", "severity": "error", "message": "Gripper stall", "payload": {"joint": 3}}'{
"event": {
"id": "e9f8d7c6-...",
"event_type": "error",
"severity": "error",
"message": "Gripper stall",
"payload": { "joint": 3 },
"created_at": "2026-07-30T05:12:01.000Z"
}
}POST /fleets — create a fleet
Session-authenticated. Creates a named group for robots (site, workflow, team). Robots start ungrouped and are assigned via PATCH /robots/:id, bulk assign, or the dashboard drag-and-drop board.
| Field | Type | Description |
|---|---|---|
| name | string, required | Fleet name, max 80 characters |
| description | string | Optional description |
| location | string | Optional site / location label |
| shift | string | Optional shift label |
| tags | string[] | Optional tags (max 20, 32 chars each) |
POST /fleets/reorder — reorder fleets
Session-authenticated. Sets fleet sort_order from the given UUID array (dashboard column drag).
| Field | Type | Description |
|---|---|---|
| order | string[], required | Fleet UUIDs in display order |
PATCH /fleets/:id — update a fleet
Session-authenticated. Updates name, description, labels (location/shift/tags), and/or sort order.
| Field | Type | Description |
|---|---|---|
| name | string | New name, max 80 characters |
| description | string | null | New description, or null to clear |
| location | string | null | Site label |
| shift | string | null | Shift label |
| tags | string[] | Replace tags array |
| sort_order | integer | Non-negative display order |
DELETE /fleets/:id — delete a fleet
Session-authenticated. Removes the fleet; its robots become ungrouped (fleet_id set to null).
POST /robots — register a robot
Session-authenticated (dashboard sign-in), not API-key authenticated. Normally you use the dashboard UI; this endpoint backs it.
| Field | Type | Description |
|---|---|---|
| name | string, required | Robot name, max 80 characters |
| description | string | Optional description |
PATCH /robots/:id — update a robot
Session-authenticated. Updates the robot's name, description, and/or fleet assignment. The API key is unchanged.
| Field | Type | Description |
|---|---|---|
| name | string | New name, max 80 characters |
| description | string | null | New description, or null to clear |
| fleet_id | string | null | UUID of a fleet you own, or null to ungroup |
POST /robots/bulk — bulk fleet assign
Session-authenticated. Assigns many robots to one fleet (or ungroups them with fleet_id: null).
| Field | Type | Description |
|---|---|---|
| robot_ids | string[], required | Robot UUIDs (max 100) |
| fleet_id | string | null | Target fleet, or null to ungroup |
POST /robots/:id/rotate-key — rotate API key
Session-authenticated. Issues a new API key and revokes the previous one. The plaintext key is returned once.
DELETE /robots/:id — delete a robot
Session-authenticated. Removes the robot and cascades its tasks. The API key stops working immediately.
GET /api/chain — network details
No authentication required. Returns the Robinhood Chain network this instance anchors to. The params object is shaped for wallet_addEthereumChain, so a browser wallet can add the network without hardcoding RPC URLs. Note that this endpoint sits at /api/chain, outside the /api/v1 prefix.
curl https://hoodly.fun/api/chain
Error format
All errors share one shape:
{
"error": "machine_readable_code",
"message": "Human-readable explanation."
}Codes you will encounter: missing_api_key, invalid_api_key, rate_limited, no_gas_credit, no_anchor_quota, invalid_json, not_found, already_completed, completion_in_progress, anchoring_failed, anchor_unconfirmed, invalid_chain_id, invalid_sample, batch_too_large, invalid_event_type, invalid_severity.
Two of those are worth handling explicitly. A 429 (rate_limited) carries a Retry-After header — see limits. A 402 means the account is out of anchors or gas credit; the task stays claimed, so retrying after a top-up completes it. See credits.
