Exploded engineering blueprint of a delivery robot

schematic sheet 01 — hoodly technical documentation

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.

FieldTypeDescription
titlestring, requiredShort task title, max 200 characters
descriptionstringOptional free-form description
expected_resultobjectOptional structured expectation, stored alongside the claim
request
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}}'
201 Created
{
  "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.

FieldTypeDescription
proofobject, requiredThe execution evidence: telemetry, results, sensor readings — any JSON structure
200 OK
{
  "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.

202 Accepted — submitted, not yet confirmed
{
  "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.

request
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.

request
curl https://hoodly.fun/api/v1/verify/0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658
200 OK
{
  "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).

FieldTypeDescription
battery_pctnumber | nullBattery charge 0–100, optional
statestring | nullConvention: idle, busy, error, charging, offline (max 64 chars)
metricsobjectArbitrary sensor readings, e.g. {"temp_c": 42, "cpu": 0.4}
recorded_atstring (ISO-8601)Optional sample time; defaults to server now
samplesarrayOptional batch of the fields above (max 50)
request
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}}'
201 Created
{
  "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.

FieldTypeDescription
event_typestring, requirede.g. error, state_change, info (max 64 chars)
severitystringinfo (default), warn, or error
messagestringHuman-readable detail, max 2000 characters
payloadobjectOptional structured context
request
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}}'
201 Created
{
  "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.

FieldTypeDescription
namestring, requiredFleet name, max 80 characters
descriptionstringOptional description
locationstringOptional site / location label
shiftstringOptional shift label
tagsstring[]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).

FieldTypeDescription
orderstring[], requiredFleet UUIDs in display order

PATCH /fleets/:id — update a fleet

Session-authenticated. Updates name, description, labels (location/shift/tags), and/or sort order.

FieldTypeDescription
namestringNew name, max 80 characters
descriptionstring | nullNew description, or null to clear
locationstring | nullSite label
shiftstring | nullShift label
tagsstring[]Replace tags array
sort_orderintegerNon-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.

FieldTypeDescription
namestring, requiredRobot name, max 80 characters
descriptionstringOptional description

PATCH /robots/:id — update a robot

Session-authenticated. Updates the robot's name, description, and/or fleet assignment. The API key is unchanged.

FieldTypeDescription
namestringNew name, max 80 characters
descriptionstring | nullNew description, or null to clear
fleet_idstring | nullUUID 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).

FieldTypeDescription
robot_idsstring[], requiredRobot UUIDs (max 100)
fleet_idstring | nullTarget 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.

request
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.