Reference
Halyard HTTP API
Nine endpoints, JSON in and JSON out, over one base URL.
https://api.halyard.dev/v1API v2.4.1Updated 13 August 2026
Authentication
Every request carries a bearer token. Tokens are scoped to one environment and one set of permissions, and Halyard never accepts a key in a query string.
curl "$API/queues" \
-H "Authorization: Bearer hk_live_2f9c…"
# 401 without it
# {"error":"unauthorized","message":"Provide a bearer token."}| queues:adminscope | Create and delete queues. Nothing else needs it. |
|---|---|
| queues:writescope | Publish, lease, extend, ack, nack and replay. This is what a consumer runs with. |
| queues:readscope | List queues and read dead letters. Never moves a message. |
Create a queue
/queues201Queues are created by name and are ready to accept messages when the call returns.
| namestringrequired | Lowercase letters, digits and hyphens, up to 64 characters. Unique inside the environment. |
|---|---|
| visibility_timeoutduration · default 30s | How long a lease hides a message. Accepts 1s to 12h; a consumer can shorten it per lease. |
| max_deliveriesinteger · default 5 | Attempts before a message moves to the dead-letter queue. Set 1 to disable retries. |
| regionstring · default the caller’s region | One of eu-west, us-east, us-west, ap-south. Fixed after creation. |
{
"name": "orders",
"region": "eu-west",
"visibility_timeout": "30s",
"max_deliveries": 5,
"depth": 0,
"created_at": "2026-08-13T09:14:22Z"
}Delete a queue
/queues/{queue}204Removes the queue, its messages and its dead letters. There is no undo and no soft delete.
HTTP/1.1 204 No Content
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1786612560Careful
Publish messages
/queues/{queue}/messages202Returns 202 once the write is durable in three availability zones.
| bodyobject | stringrequired | Stored verbatim, up to 256 KB. Objects come back as objects. |
|---|---|
| ordering_keystring | Messages sharing a key are delivered in publish order. Different keys run in parallel. |
| delayduration · default 0s | Hold the message invisible before its first delivery. Maximum 7 days. |
| Idempotency-Keyheader | Remembered for 24 hours. A repeat returns the original message instead of a duplicate. |
Fill the parameters and copy the command. It runs in your terminal; this page assembles it and never sends it.
/queues/orders/messages202curl "$API/queues/orders/messages" \
-H "Authorization: Bearer $HALYARD_KEY" \
-H "Idempotency-Key: 8f14e45f" \
-d '{"body": {"order_id":"ord_10427","total_cents":4990}, "ordering_key": "ord_10427"}'{
"id": "msg_01HQ8Z3F",
"queue": "orders",
"state": "queued",
"ordering_key": "ord_10427",
"delivery_count": 0,
"published_at": "2026-08-13T09:15:01Z"
}Lease messages
/queues/{queue}/leases200Claims up to 25 messages and hides them from other consumers until the lease expires.
| maxinteger · default 1 | Messages to lease in one call, up to 25. |
|---|---|
| waitduration · default 0s | Hold the connection open until a message arrives, up to 20s. Long polling costs nothing extra. |
| visibility_timeoutduration · default the queue’s value | Override the hide window for this lease only. |
{
"lease": "lse_01HQ8Z",
"expires_at": "2026-08-13T09:15:31Z",
"messages": [
{
"id": "msg_01HQ8Z3F",
"body": {"order_id": "ord_10427", "total_cents": 4990},
"delivery_count": 1,
"published_at": "2026-08-13T09:15:01Z"
}
]
}Note
Extend a lease
/queues/{queue}/leases/{lease}200Buys more time on work already in hand. One call per half-timeout is the pattern the SDKs use.
| extend_bydurationrequired | Added to the current expiry, not to now. Accepts 1s to 12h; the total lease life is capped at 12h. |
|---|
{
"lease": "lse_01HQ8Z",
"expires_at": "2026-08-13T09:16:01Z",
"extended_by": "30s",
"extensions": 1
}Note
Acknowledge a lease
/queues/{queue}/leases/{lease}/ack204Deletes every message in the lease. Acks are idempotent, so a retry after a timeout is safe.
HTTP/1.1 204 No Content
X-RateLimit-Remaining: 4991
X-RateLimit-Reset: 1786612560Return work early
/queues/{queue}/leases/{lease}/nack204Hands the messages back before the timeout, so a known failure does not wait out the lease.
| retry_afterduration · default the backoff schedule | Hold the message invisible for this long before redelivering. Overrides the doubling backoff once. |
|---|---|
| reasonstring | Stored on the delivery attempt and returned as last_error if the message dead-letters. Up to 200 characters. |
curl "$API/queues/orders/leases/lse_01HQ8Z/nack" \
-X POST -H "Authorization: Bearer $HALYARD_KEY" \
-d '{"retry_after": "60s", "reason": "payment_provider_timeout"}'
# 204 No ContentDead letters
/queues/{queue}/dead-letters200Lists messages that exhausted max_deliveries, with the delivery history that got them there.
{
"messages": [
{
"id": "msg_01HQ7Y2B",
"body": {"order_id": "ord_10391"},
"delivery_count": 5,
"last_error": "payment_provider_timeout",
"dead_lettered_at": "2026-08-13T08:02:44Z"
}
],
"has_more": false
}Replay a dead letter
/queues/{queue}/dead-letters/replay202Moves dead letters back to the queue. Replay resets delivery_count and preserves the ordering key.
| idsstring[] · default every dead letter | Message ids to replay. Omit to replay the whole dead-letter queue, oldest first. |
|---|---|
| maxinteger · default 100 | Messages to replay in one call, up to 1,000. |
{
"replayed": 1,
"ids": ["msg_01HQ7Y2B"],
"remaining": 0
}Errors
Errors carry a stable code, a human message, and nothing else. Retrying helps on 429 and 503; it does not on the rest.
| invalid_request400 | The body failed validation. The response names the field. Retrying will not help. |
|---|---|
| unauthorized401 | Missing or expired key. Rotate it in the dashboard, then retry. |
| queue_not_found404 | No queue by that name in this environment. Check the environment before the name. |
| lease_expired409 | The lease timed out before the ack arrived. The message is already back in the queue; do not republish it. |
| rate_limited429 | Over the ceiling. Retry-After carries the seconds to wait. |
| region_unavailable503 | A region is failing over. Retry with backoff; writes are accepted again within seconds. |
Rate limits
Limits apply per queue unless the row says otherwise. Ask support before you need a raise, not during the incident.
| Publishper queue | 1,000 messages a second sustained, 5,000 in a burst of up to ten seconds. |
|---|---|
| Leaseper queue | 500 calls a second. Batching with max raises throughput without raising this number. |
| Acknowledgeper queue | 5,000 calls a second, matched to the publish burst so an ack never becomes the bottleneck. Extend and nack share this ceiling. |
| Managementper token | 60 calls a minute across queue creation and deletion. |
| Headersevery response | X-RateLimit-Remaining and X-RateLimit-Reset are always present, including on a 200. Reset is a Unix epoch in seconds. |