RimbraDocsDashboard

Queues

Send messages to a named queue and have them delivered to your code, with retries, signatures and failed-message replay.

Create a queue

  1. Open Queues in the dashboard sidebar. Queues is a $3/month add-on on any plan; without it the page asks you to pay first and unlocks as soon as the payment is confirmed. Then choose New queue.
  2. Give it a name. Producers send to this name, so it uses lowercase letters, numbers and dashes and doesn't change later.
  3. Choose Push to a consumer and pick one of your apps plus a path, or any HTTPS address. Or choose Consumers pull if your workers will fetch messages themselves.
  4. Copy the signing secret. It is shown only once; roll a new one from the queue if it is lost.
  5. Under API tokens, create a token for the servers that send messages. It is also shown only once.

Send messages

POST to the queue with your token. The body is JSON, or text when you set contentType to text, up to 128 KB. Send up to 100 at once with a messages array.

bash
curl -X POST https://api.rimbra.app/api/queues/v1/emails/messages \
  -H "Authorization: Bearer $RIMBRA_QUEUES_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"body": {"to": "ada@example.com"}, "delaySeconds": 60, "dedupeId": "welcome-42"}'

# 202 {"id": "m_…"}
# Batch: {"messages": [{"body": …}, {"body": "text", "contentType": "text"}]}
#   202 {"messages": [{"id": "m_…"}, {"id": "m_…", "duplicate": true}]}

Message fields

FieldWhat it does
bodyThe message. Any JSON value, or a string with contentType text. Required.
contentTypejson (default) or text.
delaySecondsHold the message back for up to 43200 seconds (12 hours).
dedupeIdUp to 128 characters. Sending the same id again within 24 hours answers the first message's id and adds nothing.

Receive deliveries

Push queues post batches to your consumer as JSON: {"queue", "deliveryId", "messages": [{"id", "attempts", "createdAt", "contentType", "body"}]}. Answer with any 2xx within 30 seconds and the whole batch is done.

To finish only part of a batch, answer 2xx with {"retry": ["m_…"]}; those messages are tried again. Any other answer, or no answer, retries the whole batch.

Check the signature

Every delivery carries Rimbra-Signature: t=<unix seconds>,v1=<hex>. v1 is HMAC-SHA256 of "t.body" with the queue's signing secret. Compare it in constant time and refuse timestamps more than 5 minutes old.

js
import crypto from "node:crypto"

export async function POST(req) {
  const raw = await req.text()
  const parts = Object.fromEntries((req.headers.get("rimbra-signature") ?? "").split(",").map((p) => p.split("=")))
  const expected = crypto.createHmac("sha256", process.env.RIMBRA_QUEUE_SECRET).update(`${parts.t}.${raw}`).digest("hex")
  const a = Buffer.from(expected), b = Buffer.from(parts.v1 ?? "")
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300 || a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return new Response("bad signature", { status: 401 })
  }
  for (const m of JSON.parse(raw).messages) await handle(m.body)
  return new Response(null, { status: 204 })
}

Pull instead of push

Pull queues wait for your workers. Lease up to 100 messages, handle them, then ack by lease id. A lease that runs out puts the message back with one attempt counted; nack puts it back at once or after a delay.

bash
curl -X POST https://api.rimbra.app/api/queues/v1/jobs/lease -H "Authorization: Bearer $RIMBRA_QUEUES_TOKEN" \
  -d '{"max": 10, "visibilitySeconds": 60}'
# {"messages": [{"id": "m_…", "leaseId": "l_…", "attempts": 1, "body": …}]}

curl -X POST https://api.rimbra.app/api/queues/v1/jobs/ack  -H "Authorization: Bearer $RIMBRA_QUEUES_TOKEN" -d '{"leaseIds": ["l_…"]}'
curl -X POST https://api.rimbra.app/api/queues/v1/jobs/nack -H "Authorization: Bearer $RIMBRA_QUEUES_TOKEN" -d '{"leaseIds": ["l_…"], "delaySeconds": 30}'

Retries and failed messages

  • Each queue has attempts (1 to 20, default 5) and a first retry wait (default 10 seconds). The wait doubles each attempt, up to an hour, with a little randomness so retries don't arrive together.
  • After the last attempt, a message moves to the queue's failed messages with its last error. Replay one or all of them once your consumer is fixed; replay starts attempts from zero.
  • Pause a push queue to hold deliveries while you fix something. Messages keep arriving and wait.

What Rimbra guarantees

  • At least once: a message is removed only after your consumer answers 2xx or acks it. If an answer is lost, the message is sent again, so make handlers safe to repeat. The message id stays the same across retries.
  • Durable: messages are written to disk before the send call answers, and survive restarts. A delivery cut short by a restart is sent again.
  • Order is roughly oldest first, not strict.
  • Messages not delivered within 3 days are removed.

Price and limits

Queues is a $3/month add-on on any plan: 500,000 messages a month, up to 3 queues, kept 3 days, then $1 per extra million messages from your points. The $10/month Developer pack includes Functions, Queues, Tunnels and Buckets with double the allowances. No plan includes Queues on its own.

Messages reset each billing cycle. Past the included messages, pay as you go pays from your points; with it off, or with no points left, sends answer 429 until the next cycle. Messages waiting in your queues count toward your plan's storage.

If the add-on ends, sends answer 402 and queues can't be changed or made until you add Queues again. Messages already waiting are still delivered and can be pulled, replayed or purged, and nothing is deleted.

QueuesMessages a monthKept forPast that
Queues add-on, $3/month3500,0003 days$1 per million
Developer pack, $10/month61 million3 days$1 per million