Maintenance in progress โ€” Please try again later.

๐Ÿ•Š๏ธ

HATO API

The Pigeon Post Office

07 / Your tools, connected

API Reference

List and read Letterflows, submit runs, poll their status, and inspect regional inputs and outputs for every Letterflow step.

Choose permissions before using a key

Tinkerer keys can read, pin, and delete Letterbox entries. Aspiring keys can also read saved definitions, submit runs, and poll status when you select those scopes. A new key defaults to Letterbox reading only; a run scope does not grant polling or result access.

Overview

Authentication

Generate API keys in Settings โ†’ Integration API keys. Include your token in the Authorization header:

Choose resource and action permissions for each key. New keys allow Letterbox reading only; running, polling, pinning, and deletion require their own scopes.

Authorization: Bearer hato_live_<token>

Rate Limiting & Caching

API keys are limited to 30 requests per minute regardless of source IP. Metadata supports ETag conditional requests. Regional capability URLs expire within five minutes and follow content retention.

Error Format

Errors are returned as standard JSON objects with error codes and descriptive messages:

{"error": {"code": "unauthorized", "message": "A valid Bearer API key is required."}}

Saved definitions

List owned Letterflows with limit and cursor pagination. Detail endpoints provide metadata, readiness, and signed links to saved regional inputs. Private keys and account credentials are excluded.

EndpointAPI permissions
GET /api/v1/letterflows/
GET /api/v1/letterflows/{id}/
letterflows:read

Submit and poll a run

POST an empty JSON object to run a saved Letterflow immediately. Idempotency-Key is required: use a new key for each intended run. Repeating the same key returns the same run; overrides and API scheduling are not supported.

EndpointAPI permissions
POST /api/v1/letterflows/{id}/runs/letterflows:run
GET /api/v1/runs/{uuid}/runs:read

A new run returns 202 and a Location polling URL; an idempotent replay returns 200. States are waiting, queued, running, completed, or failed. Waiting means account capacity is not yet reserved; queued means admitted and pending regional execution. Follow letterbox_url when it becomes available. Readiness and plan limits apply to every submission.

Run polling includes queue_position, wait_reason, and admitted_at. A full waiting queue returns 429 with error.code=queue_full. An accepted idempotency key always replays the same run, including after cancellation; use a new key for a new delivery.

Delivery queues โ†’
curl -X POST -H "Authorization: Bearer $HATO_API_KEY" \
  -H 'Idempotency-Key: example-run-001' -H 'Content-Type: application/json' \
  --data '{}' 'https://hato-api.example/api/v1/letterflows/7/runs/'

Inspect Letterflow steps

Use the user-defined component ID, such as value1, in step routes. IDs are case-sensitive and resolve against the frozen execution snapshot. Later renaming does not change historical access.

GET /api/v1/letterbox/{id}/steps/
GET /api/v1/letterbox/{id}/steps/{step_id}/
GET /api/v1/letterbox/{id}/steps/{step_id}/input/
GET /api/v1/letterbox/{id}/steps/{step_id}/output/
GET /api/v1/letterbox/{id}/steps/{step_id}/request-headers/
GET /api/v1/letterbox/{id}/steps/{step_id}/request-body/
GET /api/v1/letterbox/{id}/steps/{step_id}/response-headers/
GET /api/v1/letterbox/{id}/steps/{step_id}/response-body/
GET /api/v1/letterbox/{id}/steps/{step_id}/request-metadata/
GET /api/v1/letterbox/{id}/steps/{step_id}/value/

Step inspection requires letterbox:read and redirects to the selected region. Attempted components appear in execution order, followed by skipped components. Skipped inputs remain readable; unproduced outputs return 404. Stored results are never re-evaluated.

Value outputs are text and Condition outputs are booleans. Large Values provide result_url; Letter steps provide captured request and response artifact links. Expired content returns 410.

Letter step metadata includes the recorded http_version when available. This is the final response's negotiated protocol, not the client's preference.

Send the Bearer key to Hub only. Fetch regional capability URLs with a separate request without forwarding your API key.

Letterbox lists also accept letterflow_id and run_id filters. source_type (letter, condition, value, or email) identifies the final result type, the kind of the final component.

List

GET /api/v1/letterbox/
List Letterbox entries

Retrieve a cursor-paginated list of Letterbox response metadata, ordered newest first.

Headers
Authorizationrequired Bearer hato_live_...
If-None-Match Optional ETag for 304 conditional cache validation.
Query Parameters
ParameterDescription
limitinteger Number of records to return (1โ€“100, default: 50).
cursorstring Opaque pagination cursor from previous response.
sinceISO-8601 Filter entries sent on or after datetime (e.g. 2026-08-01T00:00:00Z).
untilISO-8601 Filter entries sent on or before datetime.
letter_idinteger Filter entries by specific Letter ID.
source_typestring Filter by final result type: 'letter', 'condition', or 'value'.
status_codeinteger Filter by HTTP/FTP response code (e.g. 200, 404, 500).
pinnedboolean Filter by pinned state: 'true' or 'false'.
Responses
200 OK Returns array of entries and optional next_cursor.
400 Bad Request Invalid parameter or cursor format.
401 Unauthorized Missing or invalid Bearer API key.
cURL
curl -H "Authorization: Bearer $HATO_API_KEY" \
     "https://hato-api.example/api/v1/letterbox/?limit=20&pinned;=true"
Response (200 OK)
{
  "data": [
    {
      "id": 42,
      "letterflow_id": 7,
      "letterflow_name": "Health Check",
      "source_type": "letter",
      "run_id": null,
      "steps_url": null,
      "request": {
        "method": "GET"
      },
      "response": {
        "status_code": 200
      },
      "duration_ms": 142,
      "sent_at": "2026-08-28T14:30:00+00:00",
      "request_sent_at": "2026-08-28T14:30:00.010000+00:00",
      "response_received_at": "2026-08-28T14:30:00.152000+00:00",
      "is_pinned": true,
      "bodies_pruned": false,
      "error_message": "",
      "region": "au",
      "spoke_id": "spoke-au-1",
      "artifact_available": true,
      "request_headers_url": "https://spoke-au.example/artifacts/...",
      "request_body_url": null,
      "response_headers_url": "https://spoke-au.example/artifacts/...",
      "response_body_url": "https://spoke-au.example/artifacts/..."
    }
  ],
  "next_cursor": "eyJzZW50X2F0IjoiMjAyNi0wOC0yOFQxNDozMDowMCIsImlkIjo0Mn0"
}
DELETE /api/v1/letterbox/
Clear all unpinned entries

Deletes all unpinned Letterbox responses and immediately deletes their artifacts from regional storage. If a Spoke is temporarily unavailable, cleanup is queued for retry.

Responses
204 No Content All unpinned entries were successfully cleared.
401 Unauthorized Missing or invalid Bearer API key.
cURL
curl -X DELETE \
     -H "Authorization: Bearer $HATO_API_KEY" \
     "https://hato-api.example/api/v1/letterbox/"
GET /api/v1/letterbox/{id}/
Get entry metadata

Retrieve full metadata for a single Letterbox response by ID, including fresh signed regional artifact URLs.

URI Parameters
idintegerrequired The unique ID of the Letterbox entry.
Responses
200 OK Entry metadata successfully returned.
404 Not Found Entry does not exist or belongs to another user.
cURL
curl -H "Authorization: Bearer $HATO_API_KEY" \
     "https://hato-api.example/api/v1/letterbox/42/"
Response (200 OK)
{
  "data": {
    "id": 42,
    "letterflow_id": 7,
    "letterflow_name": "Health Check",
    "source_type": "letter",
    "run_id": null,
    "steps_url": null,
    "request": {
      "method": "GET"
    },
    "response": {
      "status_code": 200
    },
    "duration_ms": 142,
    "sent_at": "2026-08-28T14:30:00+00:00",
    "request_sent_at": "2026-08-28T14:30:00.010000+00:00",
    "response_received_at": "2026-08-28T14:30:00.152000+00:00",
    "is_pinned": false,
    "bodies_pruned": false,
    "error_message": "",
    "region": "au",
    "spoke_id": "spoke-au-1",
    "artifact_available": true,
    "request_headers_url": "https://spoke-au.example/artifacts/read?token=...",
    "request_body_url": null,
    "response_headers_url": "https://spoke-au.example/artifacts/read?token=...",
    "response_body_url": "https://spoke-au.example/artifacts/read?token=..."
  }
}
PATCH /api/v1/letterbox/{id}/
Pin or unpin entry

Pin or unpin a Letterbox entry. Pinning protects the response body from expiration and FIFO capacity pruning.

Request Body
FieldTypeDescription
is_pinnedrequired boolean Set true to pin, false to unpin.
Responses
200 OK Pin state successfully updated.
400 Bad Request Invalid JSON payload or capacity limit reached (at least 1 response must remain unpinned when at capacity).
404 Not Found Entry does not exist.
cURL
curl -X PATCH \
     -H "Authorization: Bearer $HATO_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"is_pinned": true}' \
     "https://hato-api.example/api/v1/letterbox/42/"
Response (200 OK)
{
  "data": {
    "id": 42,
    "letterflow_id": 7,
    "letterflow_name": "Health Check",
    "source_type": "letter",
    "run_id": null,
    "steps_url": null,
    "request": {
      "method": "GET"
    },
    "response": {
      "status_code": 200
    },
    "duration_ms": 142,
    "sent_at": "2026-08-28T14:30:00+00:00",
    "request_sent_at": "2026-08-28T14:30:00.010000+00:00",
    "response_received_at": "2026-08-28T14:30:00.152000+00:00",
    "is_pinned": true,
    "bodies_pruned": false,
    "error_message": "",
    "region": "au",
    "spoke_id": "spoke-au-1",
    "artifact_available": true,
    "request_headers_url": "https://spoke-au.example/artifacts/read?token=...",
    "request_body_url": null,
    "response_headers_url": "https://spoke-au.example/artifacts/read?token=...",
    "response_body_url": "https://spoke-au.example/artifacts/read?token=..."
  }
}
DELETE /api/v1/letterbox/{id}/
Delete single entry

Permanently deletes a single response record and triggers cleanup of stored artifacts from the regional Spoke.

URI Parameters
idintegerrequired The unique ID of the Letterbox entry to delete.
Responses
204 No Content Entry permanently deleted.
404 Not Found Entry does not exist.
cURL
curl -X DELETE \
     -H "Authorization: Bearer $HATO_API_KEY" \
     "https://hato-api.example/api/v1/letterbox/42/"
GET /api/v1/letterbox/{id}/{artifact}/
Download artifact stream

Returns a redirect to a short-lived regional URL. Send your Bearer key to the Hub, then fetch the regional Location separately without forwarding the key. Content grants follow retention and ownership checks.

URI Parameters
idintegerrequired Letterbox entry ID.
artifactstringrequired One of: request-headers, request-body, response-headers, response-body.
Responses
302 Found Redirects to signed direct Spoke URL (valid for 5 minutes).
404 Not Found Artifact is missing, pruned, or does not exist.
cURL (two requests)
curl -sS -D - -o /dev/null -H "Authorization: Bearer $HATO_API_KEY" \
     "https://hato-api.example/api/v1/letterbox/42/response-body/"

curl -o response_payload.json "REGIONAL_LOCATION_URL"
Redirect Headers (302 Found)
HTTP/1.1 302 Found
Location: https://spoke-au.example/artifacts/read?token=ey...
Cache-Control: private, no-store

Directory

Paid integration keys may explicitly select directory:read to list variables and obtain regional value URLs, including secrets. This permission is never automatic. Directory changes remain dashboard-only.

GET /api/v1/directory/?region=au
GET /api/v1/directory/{id}/