tailthemesbuy this theme · $39

Meridian

Design system

Eleven sections, nineteen primitives and the full token table, rendered from the files the pages import.

Tokens

Every value below comes from src/theme.css. Change it there and the whole system follows, in both modes.

  • --background#f3f7fe · #0a0e15
  • --foreground#0f141c · #e5ecf7
  • --card#ffffff · #10161f
  • --primary#1a47c7 · #7ba5f5
  • --secondary#e2eaf6 · #1a2331
  • --muted#ebf0f8 · #131a24
  • --accent#8a5a0f · #e3b778
  • --border#ccd4e2 · #2b3646
  • --ring#1a47c7 · #7ba5f5= --primary

Type scale · ratio 1.250

Anchored at 1.5rem. The classes are the system’s vocabulary; bare Tailwind steps are not used anywhere in the theme.

  • type-d3 / page title30 → 37.5pxDeliver your first message
  • type-d1 / every h224pxLease and acknowledge
  • type-read / guides prose16 → 18pxA lease is a claim on a message.
  • type-scan / reference prose16pxLimits apply per queue unless the row says otherwise.
  • type-small / interface14pxPer-queue and per-token ceilings.
  • type-label / mono slugs12pxvisibility_timeout

Radius and line

rounded-themed6pxrounded-sm4pxrounded-full999px

Spacing rhythm

3 · 4 · 6 · 8 · 10 · 12 · 16

Hairline

--border is decorative, around 1.4:1. Anything it outlines also changes fill and carries a label, so nothing depends on the line.

uisrc/components/ui/*.tsx

Nineteen primitives. Container, docs-layout, outline-rail, wire-figure, lang-tabs, log-filter and request-builder are shown in context below; the search dialog and the mode toggle are mounted once, in the header, so ⌘K and the theme class each have one owner.

GETPOSTPATCHDELETE202v2.4K
Documentation versionv2.4
  • v2.4current
  • v2.3maintained
  • v1.9archived
halyard
POST/queues/{queue}/messages202

Start here

Section heading

Eyebrow, heading and lede at the one shared type scale.

Note

An empty queue answers 200 with an empty array, not 404.

Tip

Idempotency keys are remembered for 24 hours.

Careful

A dead-letter queue with no alarm is a silent data loss policy.
POST/queues201

curl "$API/queues" \
  -H "Authorization: Bearer $HALYARD_KEY" \
  -d '{"name":"orders","visibility_timeout":"30s"}'

# 201 Created
Body parameters
namestringrequiredLowercase letters, digits and hyphens, up to 64 characters.
visibility_timeoutduration · default 30sHow long a lease hides a message. Accepts 1s to 12h.

site-headersrc/components/site-header.tsx

Skip to content
halyard
Sections menu

docs-herosrc/components/docs-hero.tsx

The indexed masthead (archetype A9): headline, first live request, and the eleven-entry index that used to be a separate card grid two screens down.

Halyard docsv2.4 current

Queues you talk to over HTTP

Publish, lease and acknowledge over one base URL; Halyard keeps ordering, retries and dead letters.

Search everything withK

POST/queues/orders/messages202

export API="https://api.halyard.dev/v1"

curl "$API/queues/orders/messages" \
  -H "Authorization: Bearer $HALYARD_KEY" \
  -H "Idempotency-Key: 8f14e45f" \
  -d '{"body": {"order_id": "ord_10427"}}'

# 202 Accepted
# {
#   "id": "msg_01HQ8Z3F",
#   "queue": "orders",
#   "state": "queued"
# }

quickstart-panelsrc/components/quickstart-panel.tsx

Four requests

From empty account to delivered

A working consumer needs one queue, one publish, one lease and one ack.

  1. Create a queueName it, set the visibility timeout, and Halyard provisions it in the caller's region.
  2. PublishSend a JSON body with an idempotency key. A duplicate key returns the original message.
  3. LeaseAsk for up to 25 messages. They stay invisible to other consumers while the lease holds.
  4. AcknowledgeAck deletes the message. Nack returns it early instead of waiting for the timeout.
first-consumer.sh

export HALYARD_KEY="hk_live_2f9c…"
export API="https://api.halyard.dev/v1"

# 1. create
curl "$API/queues" \
  -d '{"name":"orders","visibility_timeout":"30s"}'

# 2. publish
curl "$API/queues/orders/messages" \
  -H "Idempotency-Key: 8f14e45f" \
  -d '{"body":{"order_id":"ord_10427"}}'

# 3. lease up to 25 messages, wait 20s for one to arrive
curl "$API/queues/orders/leases" -d '{"max":25,"wait":"20s"}'

# 4. acknowledge the lease you were handed
curl "$API/queues/orders/leases/lse_01HQ8Z/ack" -X POST

guides-shellsrc/components/guides-shell.tsx

The three-pane frame, the reading edge, the article, and the Wire diagram figure.

Guides menu

Guides

Deliver your first message

This walkthrough takes an empty account to a consumer that acknowledges work, in about ten minutes.

Updated 13 August 20268 minute readAPI v2.4

Before you start

You need an API key with the queues:admin and queues:write scopes. Keys are scoped to one environment, and the sandbox key is free: it holds 50,000 messages and drops them after seven days.

terminal

export HALYARD_KEY="hk_sandbox_2f9c…"
export API="https://api.halyard.dev/v1"

curl "$API/queues" -H "Authorization: Bearer $HALYARD_KEY"
# {"queues": [], "has_more": false}

Every example below is a plain curl. Halyard publishes client libraries for TypeScript, Python and Go, and none of this walkthrough needs one.

Publish a message

A queue is created by name. The visibility timeout you set here is the default for every lease taken against it, and a consumer can shorten it per request.

POST/queues201

curl "$API/queues" \
  -H "Authorization: Bearer $HALYARD_KEY" \
  -d '{
    "name": "orders",
    "visibility_timeout": "30s",
    "max_deliveries": 5
  }'

# 201 Created
# {"name":"orders","region":"eu-west","created_at":"2026-08-13T09:14:22Z"}

Publishing stores the body verbatim, up to 256 KB. Halyard answers with 202 once the write is durable in three availability zones, so a successful response means the message survives a node loss.

POST/queues/orders/messages202

curl "$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"
  }'

# 202 Accepted
# {"id":"msg_01HQ8Z3F","queue":"orders","state":"queued","delivery_count":0}

Tip

Idempotency keys are remembered for 24 hours. A repeat inside that window returns the original message id instead of enqueueing a second copy, which makes a publish safe to retry after a timeout.

Lease and acknowledge

A lease is a claim. While it holds, the message is invisible to every other consumer, so two workers cannot pick up the same order. Ask for up to 25 messages at once and set wait to hold the connection open until one arrives.

POST/queues/orders/leases200

curl "$API/queues/orders/leases" \
  -H "Authorization: Bearer $HALYARD_KEY" \
  -d '{"max": 25, "wait": "20s"}'

# 200 OK
# {
#   "lease": "lse_01HQ8Z",
#   "expires_at": "2026-08-13T09:15:31Z",
#   "messages": [
#     {"id":"msg_01HQ8Z3F","body":{"order_id":"ord_10427"},"delivery_count":1}
#   ]
# }

Acknowledging deletes the message and closes the lease. Acks are idempotent, so a retried ack after a network failure returns 204 rather than an error.

POST/queues/orders/leases/lse_01HQ8Z/ack204

curl "$API/queues/orders/leases/lse_01HQ8Z/ack" \
  -X POST -H "Authorization: Bearer $HALYARD_KEY"

# 204 No Content

Work that outlives the timeout should extend the lease rather than race it. One PATCH per half-timeout is the pattern the SDKs use, and it is documented at extend a lease.

How a message moves

Five states, one loop. Everything Halyard guarantees comes from this path, so it is worth reading once before you tune anything.

  • trigger

    POST /messages

    The producer sends a body and an idempotency key. Halyard answers 202 once the write is durable.

    • step

      Durable log

      The message is written to three availability zones and ordered inside its ordering key.

      • step

        Lease held

        A consumer takes the message for visibility_timeout seconds. No one else can see it in that window.

        • check

          Acked before the timeout?

          • yes

            • end · success

              Deleted

              The message leaves the queue and the lease closes. Acks are idempotent.

          • no

            • step

              Redelivered

              delivery_count increments and the backoff doubles, up to five minutes. The message becomes visible again.

              returns to Lease held · the next consumer leases it

              • end · warning

                Dead letters

                After max_deliveries attempts the message moves to the dead-letter queue with its full history.

The path of a single Halyard message from publish to acknowledgement, including the redelivery loop and the dead-letter exit.

Handle failure

A consumer that crashes without acking is not an error condition. The lease expires, the message becomes visible again, and delivery_count increments. Nack when you already know the work failed: it returns the message immediately instead of paying for the whole timeout.

POST/queues/orders/leases/lse_01HQ8Z/nack204

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 Content

Backoff doubles from one second and stops at five minutes. After max_deliveries attempts the message moves to the queue’s dead-letter queue with its full delivery history, where you can inspect it and replay it. See dead letters for both endpoints.

Careful

A dead-letter queue with no alarm on it is a silent data loss policy. Alert on halyard.dead_letters.depth above zero, not on a threshold.

Go to production

Four settings separate a working consumer from a production one. The loop below is the shape the TypeScript SDK ships, written out so nothing is hidden.

consumer.ts

import { Halyard } from "@halyard/node";

const halyard = new Halyard({ key: process.env.HALYARD_KEY });

for await (const lease of halyard.queue("orders").leases({
  max: 25,          // batch: one round trip per 25 orders
  wait: "20s",      // long poll: no busy loop, no idle cost
  region: "eu-west" // read from the region that wrote
})) {
  for (const message of lease.messages) {
    await fulfil(message.body);
  }
  await lease.ack();
}

Ordering keys hold order inside a key and allow parallelism across keys, so one slow customer never blocks another. Retention runs to 14 days on paid plans, and a queue can be paused without losing what is already in it.

api-referencesrc/components/api-reference.tsx

Nine endpoint blocks, parameter tables and response samples in the same frame, plus the one request builder. It assembles a curl command and never sends it.

API reference menu

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.

Authorization header

curl "$API/queues" \
  -H "Authorization: Bearer hk_live_2f9c…"

# 401 without it
# {"error":"unauthorized","message":"Provide a bearer token."}
Scopes
queues:adminscopeCreate and delete queues. Nothing else needs it.
queues:writescopePublish, lease, extend, ack, nack and replay. This is what a consumer runs with.
queues:readscopeList queues and read dead letters. Never moves a message.

Create a queue

POST/queues201

Queues are created by name and are ready to accept messages when the call returns.

Body parameters
namestringrequiredLowercase letters, digits and hyphens, up to 64 characters. Unique inside the environment.
visibility_timeoutduration · default 30sHow long a lease hides a message. Accepts 1s to 12h; a consumer can shorten it per lease.
max_deliveriesinteger · default 5Attempts before a message moves to the dead-letter queue. Set 1 to disable retries.
regionstring · default the caller’s regionOne of eu-west, us-east, us-west, ap-south. Fixed after creation.
201 Created

{
  "name": "orders",
  "region": "eu-west",
  "visibility_timeout": "30s",
  "max_deliveries": 5,
  "depth": 0,
  "created_at": "2026-08-13T09:14:22Z"
}

Delete a queue

DELETE/queues/{queue}204

Removes the queue, its messages and its dead letters. There is no undo and no soft delete.

204 No Content

HTTP/1.1 204 No Content
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1786612560

Careful

A queue with a lease open still deletes. In-flight consumers get 404 on their next ack, so drain the queue before you delete it.

Publish messages

POST/queues/{queue}/messages202

Returns 202 once the write is durable in three availability zones.

Body and headers
bodyobject | stringrequiredStored verbatim, up to 256 KB. Objects come back as objects.
ordering_keystringMessages sharing a key are delivered in publish order. Different keys run in parallel.
delayduration · default 0sHold the message invisible before its first delivery. Maximum 7 days.
Idempotency-KeyheaderRemembered 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.

Build the request

the name in the path

JSON or a string, up to 256 KB

one key, publish order

1s to 7d, empty sends now

remembered for 24 hours

POST/queues/orders/messages202

curl "$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"}'
202 Accepted

{
  "id": "msg_01HQ8Z3F",
  "queue": "orders",
  "state": "queued",
  "ordering_key": "ord_10427",
  "delivery_count": 0,
  "published_at": "2026-08-13T09:15:01Z"
}

Lease messages

POST/queues/{queue}/leases200

Claims up to 25 messages and hides them from other consumers until the lease expires.

Body parameters
maxinteger · default 1Messages to lease in one call, up to 25.
waitduration · default 0sHold the connection open until a message arrives, up to 20s. Long polling costs nothing extra.
visibility_timeoutduration · default the queue’s valueOverride the hide window for this lease only.
200 OK

{
  "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

An empty queue answers 200 with an empty array, not 404. Treat the empty array as normal and keep polling.

Extend a lease

PATCH/queues/{queue}/leases/{lease}200

Buys more time on work already in hand. One call per half-timeout is the pattern the SDKs use.

Body parameters
extend_bydurationrequiredAdded to the current expiry, not to now. Accepts 1s to 12h; the total lease life is capped at 12h.
200 OK

{
  "lease": "lse_01HQ8Z",
  "expires_at": "2026-08-13T09:16:01Z",
  "extended_by": "30s",
  "extensions": 1
}

Note

Extending an expired lease answers 409, not 200. The message is already back in the queue; let the redelivery run.

Acknowledge a lease

POST/queues/{queue}/leases/{lease}/ack204

Deletes every message in the lease. Acks are idempotent, so a retry after a timeout is safe.

204 No Content

HTTP/1.1 204 No Content
X-RateLimit-Remaining: 4991
X-RateLimit-Reset: 1786612560

Return work early

POST/queues/{queue}/leases/{lease}/nack204

Hands the messages back before the timeout, so a known failure does not wait out the lease.

Body parameters
retry_afterduration · default the backoff scheduleHold the message invisible for this long before redelivering. Overrides the doubling backoff once.
reasonstringStored on the delivery attempt and returned as last_error if the message dead-letters. Up to 200 characters.
terminal

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 Content

Dead letters

GET/queues/{queue}/dead-letters200

Lists messages that exhausted max_deliveries, with the delivery history that got them there.

200 OK

{
  "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

POST/queues/{queue}/dead-letters/replay202

Moves dead letters back to the queue. Replay resets delivery_count and preserves the ordering key.

Body parameters
idsstring[] · default every dead letterMessage ids to replay. Omit to replay the whole dead-letter queue, oldest first.
maxinteger · default 100Messages to replay in one call, up to 1,000.
202 Accepted

{
  "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.

Error codes
invalid_request400The body failed validation. The response names the field. Retrying will not help.
unauthorized401Missing or expired key. Rotate it in the dashboard, then retry.
queue_not_found404No queue by that name in this environment. Check the environment before the name.
lease_expired409The lease timed out before the ack arrived. The message is already back in the queue; do not republish it.
rate_limited429Over the ceiling. Retry-After carries the seconds to wait.
region_unavailable503A 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.

Ceilings
Publishper queue1,000 messages a second sustained, 5,000 in a burst of up to ten seconds.
Leaseper queue500 calls a second. Batching with max raises throughput without raising this number.
Acknowledgeper queue5,000 calls a second, matched to the publish burst so an ack never becomes the bottleneck. Extend and nack share this ceiling.
Managementper token60 calls a minute across queue creation and deletion.
Headersevery responseX-RateLimit-Remaining and X-RateLimit-Reset are always present, including on a 200. Reset is a Unix epoch in seconds.

sdk-shellsrc/components/sdk-shell.tsx

One consumer, written three times, behind one tab strip: the selection is shared by every block on the page and a hash selects it.

Client libraries menu

SDKs

Official client libraries

Thin wrappers over the same HTTP API: batching, lease extension, backoff, and nothing else.

Updated 13 August 2026API v2.4.13 languages

What a client does

Every client is the same program in three languages: it leases a batch, hands you the bodies, and acks when your handler returns. Four things are handled below the call, and everything else is the HTTP API you already have.

Handled for you
Retries429 and 503Backoff doubles from one second and stops at five minutes. Every other code surfaces to your handler immediately, because retrying will not help.
Lease extensionhalf-timeoutOne extend call per half of the visibility timeout while a handler is still running. It stops on ack, nack or a thrown error.
Batchingmax 25One lease call per batch, long polling up to 20s, so an idle consumer holds a connection instead of spinning.
Idempotency24 hoursA publish retried after a timeout reuses its Idempotency-Key, so the message is stored once and you get the original id back.

Note

No client is required. The quickstart runs on plain curl, and a language without an official library talks to the same nine endpoints.

The same consumer, three times

One queue, batches of 25, a 20 second long poll and an ack when the handler returns. Pick a language and the page follows it.

@halyard/node 2.4.1Node 20 and Bun 1.1

The lease iterator is an async generator, so the loop ends when you break out of it and the open lease is nacked on the way out rather than left to time out.

terminal

bun add @halyard/node
consumer.ts

import { Halyard } from "@halyard/node";

const halyard = new Halyard({ key: process.env.HALYARD_KEY });

for await (const lease of halyard.queue("orders").leases({
  max: 25,
  wait: "20s"
})) {
  for (const message of lease.messages) {
    await fulfil(message.body);
  }
  await lease.ack();
}

OpenAPI spec

halyard-openapi-2.4.1.jsonOpenAPI 3.1

The spec is generated from the same source as this reference, so it carries all nine endpoints, every parameter and every error code. It ships with each release; generate a client for anything the three libraries do not cover.

halyard-openapi-2.4.1.json

{
  "openapi": "3.1.0",
  "info": {"title": "Halyard", "version": "2.4.1"},
  "servers": [{"url": "https://api.halyard.dev/v1"}],
  "paths": {
    "/queues/{queue}/messages": {
      "post": {
        "operationId": "publishMessages",
        "security": [{"bearer": ["queues:write"]}]
      }
    }
  }
}
terminal

bunx @openapitools/openapi-generator-cli generate \
  -i halyard-openapi-2.4.1.json \
  -g rust -o ./halyard-rust
Download the specsoon

published with every release

changelog-logsrc/components/changelog-log.tsx

Seven releases, newest first, filtered by tag: tagged changes stop at the reading edge, the endpoints a release added cross it as request lines.

Changelog menu

Changelog

Halyard release notes

Every change to the API, with the version you pin against and the date it shipped.

Updated 13 August 2026API v2.4.11 breaking change since v1.9

Note

The base URL carries the HTTP surface and has been /v1 since launch; the release number below is the service running behind it. A change that removes a shape gets an overlap window and appears here as breaking.

7 of 7 releases · 18 changes

v2.4.1

13 August 2026

current

fixed
The rate-limit headers were omitted from 204 responses. Acknowledge and queue delete now carry X-RateLimit-Remaining and X-RateLimit-Reset like every other call.
fixed
Extending an expired lease answered 200 with a stale expiry. It answers 409 lease_expired, and the message is already back in the queue.
changed
The error body dropped a request id field that was never populated. An error carries a stable code and a human message, and nothing else.

v2.4.0

30 July 2026

added
Extend a lease. One call per half-timeout keeps long work in hand instead of racing the visibility timeout, and the extension is added to the current expiry rather than to now.
added
Return work early with nack, and an optional retry_after that overrides the doubling backoff once.
changed
The acknowledge ceiling rose from 1,000 to 5,000 calls a second, matched to the publish burst so an ack never becomes the bottleneck.

Endpoints added

PATCH/queues/{queue}/leases/{lease}200
POST/queues/{queue}/leases/{lease}/nack204

v2.3.2

12 June 2026

fixed
Idempotency keys were forgotten after six hours instead of the documented 24, so a publish retried the next morning enqueued a second copy.
fixed
A queue deleted with a lease open left that lease answering 200. In-flight consumers now get 404 on the next ack, which is what the reference always said.

v2.3.0

21 May 2026

added
Dead letters, with the delivery history that got a message there, and replay to move it back with delivery_count reset and the ordering key preserved.
changed
max_deliveries defaults to 5, up from 3. Queues created before this release kept their own value.
fixed
A message that exhausted its deliveries during a region failover could be redelivered once more. It dead-letters on the fifth attempt.

Endpoints added

GET/queues/{queue}/dead-letters200
POST/queues/{queue}/dead-letters/replay202

v2.2.0

4 March 2026

added
ordering_key on publish. Messages sharing a key are delivered in publish order, and different keys run in parallel, so one slow customer never blocks another.
added
ap-south joins eu-west, us-east and us-west. A queue’s region is fixed after creation.
changed
Long polling holds a lease call open for up to 20 seconds, up from 5. Idle consumers cost nothing extra.

v2.0.0

14 October 2025

breaking
Leases replaced receipts. A lease call returns one lease id for the whole batch, and ack and nack take that lease instead of a per-message receipt. Both shapes were accepted for 90 days; receipts were switched off on this date.
changed
visibility_timeout is a queue setting a consumer overrides per lease, instead of a value carried on every message.
the ack that changed

# v1.9: the ack named the message, one call per message
curl "$API/queues/orders/messages/msg_01HQ8Z3F/ack" -X POST

# v2.0: the ack names the lease, and closes the whole batch
curl "$API/queues/orders/leases/lse_01HQ8Z/ack" -X POST

v1.9.0

3 June 2025

deprecated
The v1 line is closed to features. It takes security fixes until 1 June 2027, and the documentation stays online after that.
added
X-RateLimit-Remaining and X-RateLimit-Reset, backported from the v2 line so a consumer can read its headroom on either.

support-desksrc/components/support-desk.tsx

The help page, built from the same fixtures as the reference so its sample report cannot contradict it.

Support menu

Support

How to get help

What support answers, how fast it replies, and what to put in the first message.

Updated 13 August 2026First reply in one business dayAPI v2.4.1

What support answers

Support is the engineer who owns the endpoint. It answers the behaviour a reference cannot: why one queue’s leases expire early, why a message dead-lettered, whether a ceiling can move before a launch.

Read the error first. Every code Halyard returns says whether retrying helps, and the error table settles most of what arrives here before it is sent.

Tip

Search the docs before you write. PressKon any page to search every heading in the guides and the reference.

What to include

A report support can act on names one queue, one message and one moment. Without those three the first reply is a request for them, and the business day is spent asking.

Include these
queuename and environmentrequiredA name is unique inside one environment, not across them, so say whether this is sandbox or live. The region narrows it further.
message or leaseidrequiredEither prefix works. Support can read the full delivery history behind msg_ and lse_ ids; you cannot.
whenRFC 3339, UTCrequiredTo the second. Request logs are kept for 30 days, and a timestamp is what makes them searchable.
got and expectedstatus codesrequiredPaste the error body verbatim. It names the field that failed, which is usually the whole answer.
idempotency keyheaderIf the call carried one. It is how the original attempt is found behind a retry.

Six lines is a complete report. This one describes an ack that arrived after its lease had expired, which is the most common thing support sees.

a report that gets answered

POST /queues/orders/leases/lse_01HQ8Z/ack
Queue:    orders (live, eu-west)
Message:  msg_01HQ8Z3F
When:     2026-08-13T09:15:31Z
Expected: 204 No Content
Got:      409 lease_expired

When it is an incident

A 503 region_unavailable is a failover, not an outage. Retry with backoff: writes are accepted again within seconds, and a single one does not need a ticket.

Two things are worth waking someone for. A dead-letter queue that is filling, which is why the alarm belongs on halyard.dead_letters.depth above zero rather than on a threshold. And a publish still rate limited after the epoch in X-RateLimit-Reset has passed, which is the one 429 that is not your traffic.

The delivery path carries a 99.9% monthly target per region, and the availability terms say what the target covers and what a miss is worth. Support opens a claim from the same report an incident used, so send the report either way.

Careful

Do not republish a message after a 409. The lease expired, the message is already back in the queue, and republishing puts a second copy of the same order in front of a consumer.

Asking for a raise

Ceilings are per queue unless the limits table says per token. Ask before the launch rather than during it: a raise takes one business day, and an incident does not wait for one.

Send the queue, the sustained rate you expect, the shape of the burst and the region. Publish and acknowledge are separate numbers, so a consumer that keeps up at 1,000 a second still needs its own headroom to drain a backlog.

legal-desksrc/components/legal-desk.tsx

Policy, never attestation: the agreement, the use limits, the retention table and the availability target. Nothing a third party would have to certify.

Legal menu

Legal

Terms and policies

What you agree to when you call the API, and what Halyard commits to in return.

Updated 13 August 2026In force since 1 June 202530 days notice

The agreement

Creating a key is the acceptance. There is no separate signature, and the version in force is the one on this page on the day the call is made.

Your messages stay yours. Halyard stores them to deliver them, reads them only to deliver them, and never trains anything on them. You can delete a queue and its contents at any time, and the deletion is immediate rather than scheduled.

A term on this page changes with 30 days notice, sent to the account address. An API shape is a different promise and keeps its own schedule: a removed shape gets the 90 day overlap the v2.0.0 release note records, and every breaking change appears in the changelog before it ships.

Either side can end the account with 30 days notice. Halyard can end it immediately for the uses below, in which case queued messages stay readable for 14 days so you can drain them.

Acceptable use

A queue is a delivery mechanism, not storage. Publishing messages you never intend to lease, to hold data cheaply, is the one use of the API that is out of bounds by design rather than by volume.

The rest is what you would expect. Do not put someone else’s data through a queue without the right to, do not use Halyard to send unsolicited mail, and do not attempt to read a queue that is not yours. Scopes make the last one hard; the scope table says what each one opens.

Load testing is welcome and needs no permission below the published ceilings. Above them, ask support for a raise first: an unannounced burst reads as an incident and gets handled as one.

Careful

A key found in a public repository is revoked without notice. It is the one action Halyard takes on your account before telling you, because the alternative is your queue draining to somebody else.

What is stored

Five things, and nothing else. A queue’s region is fixed when it is created, and everything in the table below stays in that region for its whole life.

Retention
Message bodiesuntil ackedHeld in the queue's own region across three availability zones, and deleted when the lease that carries them is acknowledged.
Dead letters14 daysA message that exhausts max_deliveries keeps its full delivery history for 14 days, then it is deleted whether or not it was replayed.
Request logs30 daysMethod, path, status, timestamp and idempotency key. No bodies. This is what support reads when you send a timestamp.
Idempotency keys24 hoursThe key and the id it returned, so a retry inside the window answers with the original message instead of a second copy.
API keysuntil revokedStored hashed. A key is shown once, at creation, and cannot be read back afterwards by you or by Halyard.

The four regions are eu-west, us-east, us-west and ap-south. Choosing one is how residency is handled: there is no global replication to opt out of, because there is none to begin with.

Availability

The delivery path carries a 99.9% monthly target per region. Everything the target covers, and everything it pays, is in the table.

Monthly targets
Publish and lease99.9% monthlyMeasured per region on requests that reach Halyard. A 503 during a failover counts against it; a 429 against your own ceiling does not.
Management calls99.5% monthlyQueue creation and deletion. They are rarer, they are slower, and they are not on the delivery path.
Credit10% of the monthOne credit per region per month, applied to the next invoice. It is the whole remedy for a missed target.
Claim window30 daysSend the region, the window and a request id. Support opens the claim from the same report the incident used.

A single 503 region_unavailable is a failover rather than an outage, and it is measured but not usually claimable on its own. When it is an incident says what to watch and when to write.

support-stripsrc/components/support-strip.tsx

Still stuck?

Support answers API questions in one business day, with the engineer who owns the endpoint.

Or search everything withK

site-footersrc/components/site-footer.tsx

11 sections · 19 primitives · 8 pages · 54 tokens