User manual

The Queue Expert is a managed message queue. Your code keeps a WebSocket open, and each message is sent to it the moment it arrives.

Quick start

The service is not open yet. The names below, such as QUEUE-ID, are examples. You will replace them with your own values.

In about five minutes, you will send a message from one terminal and receive it in another.

What you need

When you create a queue, you get a host name and three logins: one for a publisher, one for a subscriber, and one for an administrator.

You also need websocat, a small free program. It connects to a WebSocket from a terminal. Each line you type is sent as one message.

1. Start a subscriber

A subscriber receives messages. Open a terminal, and connect:

websocat wss://QUEUE-ID.q.CLUSTER.thequeueexpert.com/

Type this line, with your subscriber's name and password, and press Enter:

{"username":"SUBSCRIBER-NAME","password":"SUBSCRIBER-PASSWORD","role":"subscriber","queuetype":"distribute","capacity":1}

The queue replies:

{"status":"Success.","sessionid":"01a1c0a0-d4d9-7000-9721-b211a887145b"}

2. Start a publisher

A publisher sends messages. Open a second terminal, connect with the same websocat command, and log in:

{"username":"PUBLISHER-NAME","password":"PUBLISHER-PASSWORD","role":"publisher","queuetype":"distribute"}
{"status":"Success."}

3. Send a message

In the publisher's terminal, type:

{"body":"Hello"}

The queue replies with the message's id:

{"id":"01a1c09f-62da-7000-a30b-0817c9a42e3f","status":"Success."}

4. Receive it

The message appears at once in the subscriber's terminal:

{"id":"01a1c09f-62da-7000-a30b-0817c9a42e3f","type":1,"body":"Hello","recdat":1794233033}

5. Acknowledge it

Tell the queue that you have finished with the message. Use the id that you received:

{"id":"MESSAGE-ID","ack":1}
{"id":"01a1c09f-62da-7000-a30b-0817c9a42e3f","status":"Success."}

The queue removes the message. To finish, log the subscriber out. The queue replies, and then closes the connection:

{"logout":true}
{"status":"Logout Success."}

How the queue works

Two queue types

Every queue has two types at the same address. You choose one each time you log in.

distributecopy
Who gets a messageOne subscriberEvery subscriber connected at that moment
Is it stored?Yes, until it is acknowledgedNo
AcknowledgementsYesNo
Delay, durability, publisher keyYesNo
AdministratorsYesNo

The rest of this section is about the distribute type.

Three roles

  • A publisher sends messages.
  • A subscriber receives messages.
  • An administrator watches and manages the queue.

A queue has one login for each role. Many connections can use the same login at the same time.

Who gets a message

Subscribers stand in a line, in the order they logged in. Each new message goes to the first subscriber in the line that has room. Messages are not spread evenly. Subscribers near the end of the line stay idle when they are not needed, so you can remove them.

A subscriber's capacity is the number of messages it can hold before it acknowledges them. Example: with a capacity of 3, a subscriber can hold 3 messages. The next one goes to the next subscriber in the line.

Acknowledge or reject

A subscriber ends its work on a message in one of two ways:

  • An ack says "done". The queue removes the message.
  • A nack says "I cannot handle this". The message moves to the dead letter queue, and it is not delivered again.

The dead letter queue (DLQ) keeps rejected messages until an administrator deletes them. An administrator can read them first.

Sessions

If a connection drops, a subscriber that has received a message can resume its session. It keeps the messages it held and gets its place in the line back. It must do this within the saved-session time limit. See Resume after a drop.

When a session ends without a resume, or a subscriber logs out, the messages it held go to the first subscriber with room. If no subscriber has room, they go to the back of the queue.

What delivery promises

  • At least once. A message can arrive more than once, for example after a resume. Use its id to find duplicates.
  • Order is not promised. Messages usually arrive in the order they were published, but a returned message goes to the back, and a delayed message waits.
  • No expiry. A message stays until it is acknowledged, rejected or deleted.

Durability

Each message has a durability level. It says how safe the message is if the machine fails.

ValueNameWhat happens
0NoneKept in memory only. It is the fastest, but the message is lost if the queue restarts. It cannot go to the dead letter queue.
1Eventual (the default)Written to the disk. A machine failure just after the write can lose it.
2ImmediateWritten to the disk and synced before the publisher gets its reply.

Connect and log in

All three roles connect and log in the same way.

The address

wss://QUEUE-ID.q.CLUSTER.thequeueexpert.com/
  • The queue speaks only WebSocket, over TLS 1.2 or newer, on port 443. Any other HTTP request gets a 400 reply.
  • The host name always has an IPv6 address. It has an IPv4 address only if your queue has one.
  • Requests and replies are JSON text, except where this manual says otherwise.

The login

The first message on a connection must be the login:

{"username":"PUBLISHER-NAME","password":"PUBLISHER-PASSWORD","role":"publisher","queuetype":"distribute"}
FieldValue
usernameYour login's name, up to 128 bytes.
passwordYour login's password, up to 128 bytes.
role"publisher", "subscriber" or "admin".
queuetype"distribute" or "copy".
contextOptional. See Matching replies.

Each role can add its own fields. See Publishers and Subscribers.

  • Send the login within 10 seconds of connecting.
  • Wait for the login's reply before you send anything else. A message sent during the login ends the connection.
  • After a failed login, the queue sends the reply and closes the connection.
  • After a login, there is no idle time limit. The connection can stay open as long as you like.

Replies

Every reply has a status, which is "Success.", "Failure." or, for a logout, "Logout Success.". A failure also has a number and a text:

{"status":"Failure.","code":203,"reason":"Invalid delay."}

In your programs, use code. The numbers do not change. reason is for people, and its words can change. The Reference lists every code. Fields can come in any order.

Matching replies

Any request can have a context: a number, or a text of up to 256 bytes. The queue copies it into the reply, so you can tell which request a reply is for.

{"body":"hello","context":7}
{"context":7,"id":"01a1c09f-62da-7000-a30b-0817c9a42e3f","status":"Success."}

Choosing a client

Any WebSocket client works. The examples use websocat, the ws package for Node.js, and the websockets package for Python.

Do not put a password in a web page. Every visitor could read it.

Why not Node.js's built-in WebSocket? When the queue sends a last reply and closes the connection at once, the built-in client can lose that reply. It unpacks compressed messages in the background, and the connection can close first. A failed login then shows only close code 1006, with no reason, and a logout shows no reply. We saw this with Node.js 26. The ws package, Python's websockets package and websocat all receive the reply.

Publishers

Log in

{"username":"PUBLISHER-NAME","password":"PUBLISHER-PASSWORD","role":"publisher","queuetype":"distribute"}
{"status":"Success."}

Publish a message

{"body":"Order 1234 is ready","delay":60,"durability":2,"publisherkey":"order-1234"}
{"id":"01a1c09f-62df-7000-84ba-6bd164337f31","status":"Success."}
FieldValue
bodyRequired. The message, as a string. To send JSON as the message, put it in the string.
type1 is text, and is the default. 2 is binary, and body must then be base64.
delaySeconds to wait before delivery, from 0 to 172800 (two days). The default is 0.
durability0, 1 or 2. The default is 1. See Durability.
publisherkeyYour own label for the message, which the subscriber receives. 1 to 128 of these characters: letters, digits, and - _ . : / + =.
contextSee Matching replies.

The biggest WebSocket message is 65536 bytes by default, and an administrator can change this. The limit includes the JSON around the body, and base64 makes a binary body about a third bigger. A bigger message closes the connection with code 1009.

Binary messages

{"body":"aGVsbG8=","type":2}

You can also send a binary WebSocket frame. The queue encodes it as base64, and the subscriber receives it with "type":2. Delay, durability and a publisher key cannot be used with this form.

Plain text

A text frame that is not a JSON object is itself the message. The frame Hello publishes the text Hello. Delay, durability and a publisher key cannot be used with this form.

A JSON object must have a body. Without one, it is refused with code 206, so a typing mistake does not become a message.

Publish in the login

A short job can log in and publish with one request. Put the message's fields in the login:

{"username":"PUBLISHER-NAME","password":"PUBLISHER-PASSWORD","role":"publisher","queuetype":"distribute","body":"in the login"}

The reply is the publish's reply. You can then close the connection.

{"id":"01a1c09f-63ed-7000-a185-6465c0a6482f","status":"Success."}

When the queue is full

Code 210 means the queue holds as many messages as it can. Code 211 means the messages use all the memory the queue has for them. Messages in the dead letter queue count too. Wait, and publish again, or ask an administrator to delete messages from the dead letter queue.

Copy queues

Log in with "queuetype":"copy". The reply says how many subscribers got the message:

{"status":"Success.","subscribers":1}

This reply has no message id. A copy queue refuses delay, durability and publisherkey.

A full program

Node.js. Install the package with npm install ws, and save the program as publisher.mjs.

import WebSocket from "ws";

const address = "wss://QUEUE-ID.q.CLUSTER.thequeueexpert.com/";
const login = {
  username: "PUBLISHER-NAME",
  password: "PUBLISHER-PASSWORD",
  role: "publisher",
  queuetype: "distribute",
};

const socket = new WebSocket(address);
let loggedIn = false;

socket.on("open", () => socket.send(JSON.stringify(login)));
socket.on("error", (error) => console.log("Connection error:", error.code || error.message));

socket.on("message", (data) => {
  const reply = JSON.parse(data);
  if (reply.status !== "Success.") {
    console.log("Failed:", reply.code, reply.reason);
    socket.close();
  } else if (!loggedIn) {
    loggedIn = true;
    socket.send(JSON.stringify({ body: "Hello from Node.js" }));
  } else {
    console.log("Published. The message id is", reply.id);
    socket.close();
  }
});

Python. Install the package with pip install websockets.

import asyncio
import json
from websockets.asyncio.client import connect
from websockets.exceptions import WebSocketException

ADDRESS = "wss://QUEUE-ID.q.CLUSTER.thequeueexpert.com/"
LOGIN = {
    "username": "PUBLISHER-NAME",
    "password": "PUBLISHER-PASSWORD",
    "role": "publisher",
    "queuetype": "distribute",
}

async def main():
    try:
        async with connect(ADDRESS) as socket:
            await socket.send(json.dumps(LOGIN))
            reply = json.loads(await socket.recv())
            if reply["status"] != "Success.":
                print("Failed:", reply["code"], reply["reason"])
            else:
                await socket.send(json.dumps({"body": "Hello from Python"}))
                reply = json.loads(await socket.recv())
                print("Published. The message id is", reply["id"])
    except (OSError, WebSocketException) as error:
        print("Connection error:", error)

asyncio.run(main())

Subscribers

Log in

{"username":"SUBSCRIBER-NAME","password":"SUBSCRIBER-PASSWORD","role":"subscriber","queuetype":"distribute","capacity":10}
{"status":"Success.","sessionid":"01a1c09f-64be-7000-99d3-a74b8cef2465"}

capacity is required, from 1 to the max-subscriber-capacity setting, which is 1024 by default. Keep the sessionid: you need it to resume. Every login gives a new one.

What you receive

Three kinds of frame arrive on the connection. All are JSON text.

A message has a body:

{"id":"01a1c09f-62e2-7000-b616-7c246d151566","publisherkey":"order-1234","type":1,"body":"Order 1234 is ready","recdat":1794233033}
FieldValue
idThe message's id. Ids sort in time order.
publisherkeyOnly if the publisher gave one.
type1 is text, 2 is binary. A binary body is base64.
bodyThe message.
recdatWhen the queue received it, in seconds since 1970 (UTC).

A reply to something you sent has a status.

A notice says that an administrator took away a message you hold. Stop working on it, and do not acknowledge it:

{"deleted":true,"id":"01a1c09f-62da-7000-a30b-0817c9a42e3f"}
{"movedtodlq":true,"id":"01a1c09f-62da-7000-a30b-0817c9a42e3f"}

Acknowledge or reject

To acknowledge, send "ack":1. To reject, which moves the message to the dead letter queue, send "ack":0.

{"id":"MESSAGE-ID","ack":1}
{"id":"MESSAGE-ID","ack":0}
{"id":"01a1c09f-62da-7000-a30b-0817c9a42e3f","status":"Success."}

Code 301 means you do not hold that message. A message with durability 0 cannot be rejected, because it is not on the disk. The queue refuses with code 305. Acknowledge it instead.

Change the capacity

{"capacity":2}
{"status":"Success."}

If the new capacity leaves room, messages that are waiting are sent at once.

Resume after a drop

Log in again with your last sessionid. You can leave out capacity to keep the one you had.

{"username":"SUBSCRIBER-NAME","password":"SUBSCRIBER-PASSWORD","role":"subscriber","queuetype":"distribute","sessionid":"SESSION-ID","replay":true}
{"status":"Success.","sessionid":"01a1c09f-9b9c-7000-9c23-6bf133d5b10e"}

With "replay":true, the queue sends again every message you hold. With "replay":false or no replay, it sends only the ones it never sent you.

The time limit is 10 seconds by default, and one hour on the Dev/test tier. An administrator can set it from 10 seconds to one day.

A session is saved when a connection drops if the subscriber holds a message, or has received one in the past. A resume fails with code 109, and the connection closes, in these cases:

  • The queue still sees your old connection as open. This can happen just after a network failure.
  • Another connection is resuming the same session at that moment.
  • The time limit has passed.
  • The subscriber logged out.
  • The subscriber never received a message.
  • The subscriber held no message when it dropped, and the queue already has max-tasks saved sessions.

Because of the first two, try the resume again a few times, a few seconds apart. If it still fails, connect and log in with a capacity and no sessionid. You go to the end of the line.

Log out

{"logout":true}
{"status":"Logout Success."}

The queue replies, and then closes the connection with code 1000. The messages you still hold go to other subscribers.

Wait for the reply to every ack before you log out. If you do not, the ack still counts, but its reply can be lost, and the message can be delivered again.

Copy queues

Log in with "queuetype":"copy" and no capacity. You receive each message that is published while you are connected. You send nothing after the login, and there is no ack. A JSON frame is refused with code 306. A frame that is not JSON gets code 100, and the connection closes.

A full program

Each program handles every message and acknowledges it. A real program should also connect again if the connection drops, and resume.

Node.js. Save it as subscriber.mjs.

import WebSocket from "ws";

const address = "wss://QUEUE-ID.q.CLUSTER.thequeueexpert.com/";
const login = {
  username: "SUBSCRIBER-NAME",
  password: "SUBSCRIBER-PASSWORD",
  role: "subscriber",
  queuetype: "distribute",
  capacity: 10,
};

const socket = new WebSocket(address);

socket.on("open", () => socket.send(JSON.stringify(login)));
socket.on("error", (error) => console.log("Connection error:", error.code || error.message));

socket.on("message", (data) => {
  const frame = JSON.parse(data);
  if (frame.body !== undefined) {
    console.log("Received:", frame.body);
    socket.send(JSON.stringify({ id: frame.id, ack: 1 }));
  } else if (frame.status === "Failure.") {
    console.log("Failed:", frame.code, frame.reason);
  }
});

Python.

import asyncio
import json
from websockets.asyncio.client import connect
from websockets.exceptions import WebSocketException

ADDRESS = "wss://QUEUE-ID.q.CLUSTER.thequeueexpert.com/"
LOGIN = {
    "username": "SUBSCRIBER-NAME",
    "password": "SUBSCRIBER-PASSWORD",
    "role": "subscriber",
    "queuetype": "distribute",
    "capacity": 10,
}

async def main():
    try:
        async with connect(ADDRESS) as socket:
            await socket.send(json.dumps(LOGIN))
            async for text in socket:
                frame = json.loads(text)
                if "body" in frame:
                    print("Received:", frame["body"], flush=True)
                    await socket.send(json.dumps({"id": frame["id"], "ack": 1}))
                elif frame.get("status") == "Failure.":
                    print("Failed:", frame["code"], frame["reason"], flush=True)
    except (OSError, WebSocketException) as error:
        print("Connection error:", error, flush=True)

asyncio.run(main())

Administrators

Log in with the role "admin" and the queue type "distribute". Then send commands. Each command has a cmd field, and can have a context.

{"username":"ADMIN-NAME","password":"ADMIN-PASSWORD","role":"admin","queuetype":"distribute"}
{"status":"Success."}

Watch the queue

getlivestats gives the counters right now:

{"cmd":"getlivestats"}
{"status":"Success.","csv":"10,0,1,0,1,3,0,1,9"}

The columns of csv, in order: messages, publishers, subscribers, saved subscribers, administrators, unacknowledged, dead letter, delayed, durable. "Messages" counts every message the queue holds: waiting, held, delayed, and in the dead letter queue.

Add subscribe to get the counters pushed to you every so many seconds, from 30 to 900. Each push has "cmd":"getlivestats" and a csv. A value of 0 stops the pushes. Pushes stop when the connection closes.

{"cmd":"getlivestats","subscribe":30}

getstats gives the history of one day, in UTC. With "average":false you get every record of the day, and with true you get one line of averages. Each line is the time in seconds since 1970, then the same nine columns.

{"cmd":"getstats","average":false,"year":2026,"month":11,"day":9}

getusage gives the usage of one day, in UTC. With "totals":false you get every record of the day, and with true you get one line for the whole day.

{"cmd":"getusage","totals":true,"year":2026,"month":11,"day":9}
{"status":"Success.","csv":"1794182400,0,0,0,0,4096,m4.xlarge,us-east-1,1\n"}

The columns: time, network bytes sent, network bytes received, storage bytes written, storage bytes read, storage bytes held, instance type, region, and 1 if the queue has an IPv4 address. In the day's totals, the bytes are added up and the storage is an average.

Manage messages

getmsgs lists the messages in the queue: waiting, held and delayed. Give exactly one of after or before, with a message id. An empty string means the start (for after) or the end (for before). count is from 1 to 1600.

{"cmd":"getmsgs","after":"","count":20}
{"status":"Success.","csv":"01a1c09f-62da-7000-a30b-0817c9a42e3f,89,0,1,1,1794233033\n"}

The columns: id, length in bytes, delay, durability, type, time received.

getmsg shows one message, with its content. It does not change the message.

{"cmd":"getmsg","id":"MESSAGE-ID"}
{"status":"Success.","id":"01a1c09f-62da-7000-a30b-0817c9a42e3f","msg":{"id":"01a1c09f-62da-7000-a30b-0817c9a42e3f","type":1,"body":"hello","recdat":1794233033},"len":89,"delay":0,"durability":1}

addmsg publishes a message. It takes the same fields as a publish, and the reply has the new id.

{"cmd":"addmsg","body":"Added by an administrator"}
{"id":"01a1c09f-827a-7000-b75c-99301d00e385","status":"Success."}

delmsg deletes a message that is waiting, held or delayed. movetodlq moves one to the dead letter queue. A message with durability 0 cannot be moved, and is refused with code 413. If a subscriber holds the message, it gets a notice.

{"cmd":"delmsg","id":"MESSAGE-ID"}
{"cmd":"movetodlq","id":"MESSAGE-ID"}

The dead letter queue

getdlqmsgs lists it, and works like getmsgs. getdlqmsg shows one message, and works like getmsg. deldlqmsg deletes one.

{"cmd":"getdlqmsgs","after":"","count":20}
{"cmd":"getdlqmsg","id":"MESSAGE-ID"}
{"cmd":"deldlqmsg","id":"MESSAGE-ID"}

If a message's file is damaged on the disk, the queue moves it here. Its msg is then {"id":"..","damaged":true,"file":".."}, where file is the whole file in base64. file is left out when the file is too big.

Put a message back in the queue

There is no single command for this. Use three:

  1. getdlqmsg to read the message.
  2. addmsg to publish it again, with the same body and type. It gets a new id.
  3. deldlqmsg to remove the old copy. Do this last. If your program stops before this step, the message is in both places, and nothing is lost.

Settings

getcfg shows every setting as a name, with "r" (read only) or "w" (you can change it), and its value.

{"cmd":"getcfg"}

setcfg changes one or more settings. The change takes effect at once, and is kept after a restart. The Reference lists the settings.

{"cmd":"setcfg","saved-session-timeout-secs":60}
{"status":"Success."}

A full program

Each program prints the queue's live counters.

Node.js. Save it as admin.mjs.

import WebSocket from "ws";

const address = "wss://QUEUE-ID.q.CLUSTER.thequeueexpert.com/";
const login = {
  username: "ADMIN-NAME",
  password: "ADMIN-PASSWORD",
  role: "admin",
  queuetype: "distribute",
};

const socket = new WebSocket(address);
let loggedIn = false;

socket.on("open", () => socket.send(JSON.stringify(login)));
socket.on("error", (error) => console.log("Connection error:", error.code || error.message));

socket.on("message", (data) => {
  const reply = JSON.parse(data);
  if (reply.status !== "Success.") {
    console.log("Failed:", reply.code, reply.reason);
    socket.close();
  } else if (!loggedIn) {
    loggedIn = true;
    socket.send(JSON.stringify({ cmd: "getlivestats" }));
  } else {
    console.log("Messages in the queue:", reply.csv.split(",")[0]);
    socket.close();
  }
});

Python.

import asyncio
import json
from websockets.asyncio.client import connect
from websockets.exceptions import WebSocketException

ADDRESS = "wss://QUEUE-ID.q.CLUSTER.thequeueexpert.com/"
LOGIN = {
    "username": "ADMIN-NAME",
    "password": "ADMIN-PASSWORD",
    "role": "admin",
    "queuetype": "distribute",
}

async def main():
    try:
        async with connect(ADDRESS) as socket:
            await socket.send(json.dumps(LOGIN))
            reply = json.loads(await socket.recv())
            if reply["status"] != "Success.":
                print("Failed:", reply["code"], reply["reason"])
            else:
                await socket.send(json.dumps({"cmd": "getlivestats"}))
                reply = json.loads(await socket.recv())
                print("Messages in the queue:", reply["csv"].split(",")[0])
    except (OSError, WebSocketException) as error:
        print("Connection error:", error)

asyncio.run(main())

Reference

Limits and settings

An administrator can change these with setcfg.

SettingDefaultRangeWhat it is
max-ws-msg-len655361024 to 1048576The biggest WebSocket message, in bytes, including the JSON around the body.
max-subscriber-capacity10241 to 1024The biggest capacity a subscriber can ask for.
saved-session-timeout-secs1010 to 86400How long a dropped session can be resumed. One hour on Dev/test.
no-reply-timeout-secs101 to 300How long a new connection has to log in.
stats-frequency-secs300300 to 3600How often a stats record is made. One hour on Dev/test.
stats-retention-period-yrs51 to 10How long stats are kept. One year on Dev/test.
usage-frequency-secs18001800 to 86400How often a usage record is made.
usage-retention-period-yrs51 to 20How long usage is kept.

These can only be read, with getcfg:

SettingWhat it is
max-tasksThe most connections at one time. 100 on Dev/test. On other tiers, it depends on the machine's memory.
max-messagesThe most messages the queue can hold, counting the dead letter queue. It depends on the machine's memory, and is never more than 1,000,000.
max-messages-memoryThe memory the queue can use for messages, in bytes.
messages-memory-usedThe memory messages use now, in bytes.
max-write-queue-bytesHow many bytes can wait to be sent to one connection. The default is 4 MiB, and 1 MiB on Dev/test. A client that reads too slowly and goes over this is disconnected.
message-timeout-secsA message sent in several WebSocket frames must arrive whole within this time. The default is 30 seconds.
log-levelHow much the queue writes to its own log.

Why a connection closed

Close codeMeaning
1000You logged out.
1002The client broke the WebSocket protocol.
1007A text frame was not valid UTF-8.
1008A message did not arrive whole within message-timeout-secs.
1009A message was bigger than max-ws-msg-len.
1011A compressed message could not be unpacked.

After a failed login, or text that is not JSON from a subscriber or an administrator, the queue sends a reply and closes the connection with no close code. Your client may report this as 1006. When every connection slot is in use, a new connection is closed with no message.

Failure codes

The 500s are the queue's own problems, and trying again later is usually sensible. Codes 506 and 507 are different: the change was made, but it may not survive a machine failure. Do not send it again. If you do, a message is published twice.

Connecting and logging in

CodeReason
100Invalid json.
101Invalid context.
102Login message must be of type text, not binary.
103Invalid username.
104Invalid password.
105Invalid role.
106Invalid queue type.
107Authentication failed. Invalid combination of username, password and role.
108Admin login is only allowed for distribute type queue.
109Invalid session id.
110Invalid "replay".

Publishing

CodeReason
200Invalid message body.
201Invalid message type.
202Body must be base64 encoded for binary messages.
203Invalid delay.
204Invalid durability.
205Invalid publisher key.
206Cannot publish: a JSON object must have a body field. To send JSON as the message, put it in body as a string.
207Delay is only allowed for distribute type queue.
208Durability is only allowed for distribute type queue.
209Publisher key is only allowed for distribute type queue.
210Max allowed message count exceeded.
211Max allowed message memory exceeded.

Subscribers

CodeReason
300Invalid capacity.
301Invalid message id.
302Invalid ack.
303Invalid logout.
304Invalid message. Expecting ack or logout or capacity update.
305Cannot nack: a message with durability 0 is not on the disk, so it cannot go to the dead letter queue. Ack it instead.
306No incoming messages are allowed after successful login for copy queue subscribers.

Administrators

CodeReason
400Invalid command (cmd).
401Invalid subscription period. Must be zero, or between 30 and 900 (inclusive).
402A subscription is already active. Cancel it first by using value 0 for subscribe.
403No active subscription to cancel.
404Invalid year.
405Invalid month.
406Invalid day.
407Invalid date, don't have data before (a time).
408Invalid average (or totals), not specified. Or: must be boolean.
409Exactly one of before/after must be specified.
410Invalid before.
411Invalid after.
412Invalid message count. Or: Count must be a value between 1 and 1600 (both inclusive).
413Cannot move: a message with durability 0 is not on the disk, so it cannot go to the dead letter queue. Use delmsg instead.
414Invalid (setting). Must be between (its limits).
415Read only parameter (setting) specified.
416No valid parameter with write permission specified.

The queue's own problems

CodeReason
500Internal error.
501Out of memory.
502Database is down.
503Database server did not respond in time.
504Unknown failure.
505Could not save the settings. Nothing has changed.
506Settings changed, but the save may not survive a machine crash.
507Sync to file failed. This operation has eventual durability.

Coming from Amazon SQS

The main difference is that you do not poll. Your subscriber keeps a WebSocket open, and each message is sent to it the moment it arrives.

Amazon SQSThe Queue Expert
SendMessageA publish on an open connection
ReceiveMessage, long pollingNone. Messages are pushed to you.
DeleteMessageAn ack
Visibility timeoutNone. A subscriber holds a message until it answers or goes away.
MaxNumberOfMessagescapacity, which limits how many messages a subscriber holds
Delay of up to 15 minutesdelay of up to two days
Retention of up to 14 daysNo expiry. A message stays until it is handled.
Dead-letter queue, after too many receivesDead letter queue, after a nack. The queue does not count deliveries.
DLQ redriveThree commands
Message attributesPut them in body, or use publisherkey for one label
Batch requestsNone. Send messages one at a time on the open connection.
PurgeQueueNone. Delete messages one at a time with delmsg.
Amazon SNS fan-outA copy queue
HTTPS APIWebSocket only