REST Overview
REST Endpoints, Authentication, Compression and Error Responses
REST API
Section titled “REST API”Base URL: http://localhost:21000 or http://<node-ip>:21000
For non-localhost or unsecured LAN deployments, TLS termination via reverse proxy is recommended:
https://<node-ip>
Endpoints
Section titled “Endpoints”| Method | Endpoint | Description |
|---|---|---|
GET | /version | blockd and connected service versions |
GET | /health | Server health |
GET | /health/{level} | Health at basic, tip, or detailed verbosity |
GET | /workflows | List queued and running workflows |
GET | /workflows/{id} | Get a specific workflow |
POST | /workflows | Submit a new workflow |
DELETE | /workflows/{id} | Cancel or delete a workflow |
GET | /workflows/{id}/results/core | Results of a core.* workflow |
POST | /pubkeys-set/txns | Query transactions for a pubkeys set |
POST | /pubkeys-set/utxos | Query UTXOs for a pubkeys set |
WS | /events | Subscribe to real-time events |
Authentication
Section titled “Authentication”All endpoints require a Bearer token except /events, which uses WebSocket authentication.
See WebSocket Authentication for details.
Authorization: Bearer REPLACE_ME_secret_tokenTokens must be between 16 and 64 characters.
Endpoints are grouped into two auth tiers:
- Auth only —
/health,/version. Available before blockd is fully ready. - Auth + ready —
/workflows,/pubkeys-set. Requires blockd state to beready.
A missing or invalid token returns 401 with an empty response body.
Compression
Section titled “Compression”blockd supports response compression using gzip and zstd. Request compression by sending the standard
Accept-Encoding header:
Accept-Encoding: gzip, zstdblockd picks the first algorithm from the Accept-Encoding list that it supports,
compresses the response, and sets the appropriate Content-Encoding header. Most HTTP
clients decompress automatically. If Accept-Encoding is absent, responses are sent
uncompressed.
Error Responses
Section titled “Error Responses”All structured error responses use a common envelope:
{ "error": "description of the error", "error_code": "ERROR_CODE"}Internal server errors additionally include an error_id for log correlation:
{ "error": "internal server error", "error_code": "INTERNAL_ERROR", "error_id": "ERR-a4Xk9mPq"}Report the error_id when contacting support — it maps to the full error detail in the
server logs without exposing internal structures in the API response.
Error codes:
| Status | error_code | Description |
|---|---|---|
400 | INVALID_REQUEST | Malformed request body |
400 | INVALID_STEP | Unknown or invalid workflow step |
400 | INVALID_ARGUMENT | Invalid step argument |
400 | VALIDATION_ERROR | Request body validation failure |
401 | — | Missing or invalid auth token ¹ |
404 | NOT_FOUND | Resource not found |
500 | INTERNAL_ERROR | Internal server error ² |
| 503 | NOT_READY | blockd not yet ready — check Retry-After header |
¹ 401 returns an empty response body — no JSON envelope.
² 500 responses include error_id. Internal error details are logged server-side and
never exposed in the API response.
³ A 503 response is returned when blockd is not yet ready to serve requests.
The standard error envelope is used:
{ "error": "server not ready", "error_code": "NOT_READY"}The Retry-After header indicates how many seconds to wait before retrying:
Retry-After: 15Example — malformed workflow submission:
{ "error": "Failed to parse the request body as JSON: workflow.steps[0].?: trailing comma at line 11 column 7", "error_code": "INVALID_REQUEST"}