raresats.net API
Chat & profiles API View as Markdown

Chat messages

Messages are filed under topics (just General for now), each with threads one level deep: a reply to a reply joins its root's thread, in its topic. Without a topic, a request is General's. Messages and replies arrive live as chat events on the event stream, each naming its topic. Where the AI moderation is on, every message is checked before it is posted: Moderation.

List topics #

GET/api/chat/topics

The live topics, in their order, each with its newest message's id and time: a page keeps the last id it read per topic and shows the others as unread. New and archived topics come as chat events {type: "topic", topic, archived}.

Example request #

cURL
curl 'https://raresats.net/api/chat/topics'
JavaScript
const response = await fetch('https://raresats.net/api/chat/topics');
const data = await response.json();
Python
import requests

data = requests.get("https://raresats.net/api/chat/topics").json()

Response 200 #

FieldTypeDescription
topicsarray of object
topics[].idstring1-32 of a-z, 0-9 and inner hyphens.
topics[].namestring1-40 characters, plain text.
topics[].descriptionstringAt most 140 characters, plain text; may be empty.
topics[].positionintegerIts place in the list.
topics[].created_atintegerUnix seconds.
topics[].last_message_idinteger | nullThe newest live message's id (a reply counts); null when it has none. A page that keeps the last id it read per topic shows a topic as unread when this is larger.
topics[].last_activity_atinteger | nullThat message's time, unix seconds; null when it has none.
Response
{
  "topics": [
    {
      "id": "general",
      "name": "General",
      "description": "Anything rare-sat: say gm, ask, share a find.",
      "position": 0,
      "created_at": 1790900000,
      "last_message_id": 1044,
      "last_activity_at": 1790949460
    },
    {
      "id": "market",
      "name": "Market & prices",
      "description": "Listings, offers, floors and what things sell for.",
      "position": 1,
      "created_at": 1790900000,
      "last_message_id": 1042,
      "last_activity_at": 1790949400
    }
  ]
}

Errors #

{error, code}; see Errors for every code.

StatusCodeMeaning
404chat_disabledChat and display names are off on this server.
405method_not_allowedWrong method for the route; the allow header lists the right ones.

Create a topic (admin) #

POST/api/chat/topics

An admin session creates a topic, last in the list. Only admins may: topic names are not run through the AI moderation (an admin's text never is), but follow a message's text rules (NFC, no control, zero-width or bidi characters) and its link rule. An id is never reused, an archived topic's included, except a retired one's (a topic the defaults once seeded and no longer name): it comes back as the new topic, its messages with it. At most 50 topics. Pages are told with a chat event {type: "topic", topic, archived: false}. Logged.

Request body #

content-type: application/json

FieldTypeRequiredDescription
idstringyes1-32 of a-z, 0-9 and inner hyphens; not main.
namestringyes1-40 characters.
descriptionstring | nullnoAt most 140 characters.

Example request #

cURL
curl -X POST 'https://raresats.net/api/chat/topics' \
  -H 'content-type: application/json' \
  -d '{
  "id": "runes",
  "name": "Runes",
  "description": "Runes on rare sats, and what they do to a sat'\''s value."
}'
JavaScript
const response = await fetch('https://raresats.net/api/chat/topics', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    "id": "runes",
    "name": "Runes",
    "description": "Runes on rare sats, and what they do to a sat's value."
  })
});
const data = await response.json();
Python
import requests

data = requests.post("https://raresats.net/api/chat/topics", json={
    "id": "runes",
    "name": "Runes",
    "description": "Runes on rare sats, and what they do to a sat's value."
}).json()

Response 200 #

FieldTypeDescription
topicobjectA chat topic (a channel). Its id is its slug: the page is /chat/<id>.
topic.idstring1-32 of a-z, 0-9 and inner hyphens.
topic.namestring1-40 characters, plain text.
topic.descriptionstringAt most 140 characters, plain text; may be empty.
topic.positionintegerIts place in the list.
topic.created_atintegerUnix seconds.
topic.last_message_idinteger | nullThe newest live message's id (a reply counts); null when it has none. A page that keeps the last id it read per topic shows a topic as unread when this is larger.
topic.last_activity_atinteger | nullThat message's time, unix seconds; null when it has none.

Errors #

{error, code}; see Errors for every code.

StatusCodeMeaning
400bad_jsonThe body is not valid JSON.
400bad_requestThe body is not a JSON object, or the engine refused a malformed request (e.g. a purchase submitted without its signed setup PSBT).
400bad_topicA topic id that is not one: 1-32 of a-z, 0-9 and inner hyphens (and, to create one, not main). On a reply: a topic other than its root's.
401no_sessionNo chat session: sign in (POST /api/session) and send its cookie.
403cross_originA POST from a browser on another site (a foreign Origin, or sec-fetch-site: cross-site). Server-side clients send no Origin and are not affected.
403not_adminOnly a chat admin (SUPPLY_CHAT_ADMINS) may do that.
404chat_disabledChat and display names are off on this server.
405method_not_allowedWrong method for the route; the allow header lists the right ones.
409topic_existsA topic (or an archived one) has that id already.
413too_largeThe body is over 256 KiB.
415not_jsonA POST without content-type: application/json.
422bad_topic_textA topic's name must be 1-40 characters, its description at most 140.
422suspicious_linkA link in the message names a host that is not plain ASCII: look-alike letters from other scripts, punycode (xn--), or user@host. Write the link with its real host name.
422too_many_topicsAt most 50 chat topics: archive one first.
429rate_limitedOver 30 POSTs a minute from your IPv4 address or IPv6 /64 (in chat, counted per IPv4 address or IPv6 /64: 30 writes and 20 login nonces a minute, 20 sign-ins and 10 display-name changes an hour; one message every 3 s and 20 a minute per address); wait retry-after seconds. With the AI moderation on, at most 6 messages and names a minute are checked per address and per IPv4 address or IPv6 /48.

Archive a topic (admin) #

DELETE/api/chat/topics/{id}

An admin session archives a topic: it leaves the list and takes no new messages or replies (404 unknown_topic); its messages stay in the store. General cannot be archived. Pages are told with a chat event {type: "topic", topic, archived: true}. Logged.

Path parameters #

ParameterTypeRequiredDescription
idstringyesThe topic's id.

Example request #

cURL
curl -X DELETE 'https://raresats.net/api/chat/topics/off-topic' -H 'cookie: __Host-rs_chat=<token>'
JavaScript
const response = await fetch('https://raresats.net/api/chat/topics/off-topic', { method: 'DELETE', credentials: 'include' });
const data = await response.json();

Response 200 #

FieldTypeDescription
topicobjectA chat topic (a channel). Its id is its slug: the page is /chat/<id>.
topic.idstring1-32 of a-z, 0-9 and inner hyphens.
topic.namestring1-40 characters, plain text.
topic.descriptionstringAt most 140 characters, plain text; may be empty.
topic.positionintegerIts place in the list.
topic.created_atintegerUnix seconds.
topic.last_message_idinteger | nullThe newest live message's id (a reply counts); null when it has none. A page that keeps the last id it read per topic shows a topic as unread when this is larger.
topic.last_activity_atinteger | nullThat message's time, unix seconds; null when it has none.
archivedtrue

Errors #

{error, code}; see Errors for every code.

StatusCodeMeaning
400bad_pathThe path is not valid percent-encoding.
400bad_topicA topic id that is not one: 1-32 of a-z, 0-9 and inner hyphens (and, to create one, not main). On a reply: a topic other than its root's.
401no_sessionNo chat session: sign in (POST /api/session) and send its cookie.
403cross_originA POST from a browser on another site (a foreign Origin, or sec-fetch-site: cross-site). Server-side clients send no Origin and are not affected.
403not_adminOnly a chat admin (SUPPLY_CHAT_ADMINS) may do that.
404chat_disabledChat and display names are off on this server.
404unknown_topicNo such chat topic, or an archived one (which takes no new messages).
405method_not_allowedWrong method for the route; the allow header lists the right ones.
422not_archivableGeneral cannot be archived.
429rate_limitedOver 30 POSTs a minute from your IPv4 address or IPv6 /64 (in chat, counted per IPv4 address or IPv6 /64: 30 writes and 20 login nonces a minute, 20 sign-ins and 10 display-name changes an hour; one message every 3 s and 20 a minute per address); wait retry-after seconds. With the AI moderation on, at most 6 messages and names a minute are checked per address and per IPv4 address or IPv6 /48.

List messages #

GET/api/chat/messages

A topic's messages (the threads' roots), newest first, with each author's current display name and each thread's reply count and last reply time. Without topic, General's (main, the one room before topics, names General too). Replies are on their thread: List a thread. Page back with before. Messages are kept 30 days, at most 10,000 a topic.

Query parameters #

ParameterTypeRequiredDescription
topicstringnoA topic id (List topics); default general. Default: general.
beforeintegernoA message id: only older ones (the previous page's next). (min 1)
limitintegernoMessages per page, 1-100. Default: 50. (min 1, max 100)

Example request #

cURL
curl 'https://raresats.net/api/chat/messages?topic=market&limit=50'
JavaScript
const response = await fetch('https://raresats.net/api/chat/messages?topic=market&limit=50');
const data = await response.json();
Python
import requests

data = requests.get("https://raresats.net/api/chat/messages", params={"topic": "market", "limit": 50}).json()

Response 200 #

FieldTypeDescription
topicstringThe topic.
roomstringDeprecated: the same as topic.
messagesarray of objectThe topic's threads' roots, newest first (replies are on their thread).
messages[].idintegerIncreasing; the paging cursor.
messages[].topicstringThe topic it is in (a reply: its root's).
messages[].roomstringDeprecated: the same as topic (the one room before topics was main, now general).
messages[].addressstringThe author.
messages[].display_namestring | nullThe author's display name now.
messages[].adminbooleanThe author is a chat admin.
messages[].textstringPlain text (NFC, no control, zero-width or bidi characters, one line, 1-500 characters). Show it as text, never as HTML.
messages[].created_atintegerUnix seconds.
messages[].reply_tointeger | nullThe thread's root, on a reply; null on a root (a message of the topic).
messages[].reply_countintegerOn a root: its live replies (0 on a reply).
messages[].last_reply_atinteger | nullOn a root: its newest reply's time, unix seconds; null without replies.
messages[].quoteobject | nullOn a reply: the message it answered (the root, or a reply in the thread), while it is live; text only, cut at 140 characters. Null on a root.
messages[].quote.idinteger
messages[].quote.addressstring
messages[].quote.display_namestring | null
messages[].quote.textstring
nextinteger | nullPass as before for the older page; null at the start.
Response
{
  "topic": "market",
  "room": "market",
  "messages": [
    {
      "id": 1042,
      "topic": "market",
      "room": "market",
      "address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
      "display_name": "Rare Hunter",
      "admin": false,
      "text": "gm, any block 9 for sale?",
      "created_at": 1790949400,
      "reply_to": null,
      "reply_count": 2,
      "last_reply_at": 1790949460,
      "quote": null
    }
  ],
  "next": 1042
}

Errors #

{error, code}; see Errors for every code.

StatusCodeMeaning
400bad_paginglimit or offset out of range.
400bad_topicA topic id that is not one: 1-32 of a-z, 0-9 and inner hyphens (and, to create one, not main). On a reply: a topic other than its root's.
404chat_disabledChat and display names are off on this server.
404unknown_topicNo such chat topic, or an archived one (which takes no new messages).
405method_not_allowedWrong method for the route; the allow header lists the right ones.

List a thread #

GET/api/chat/messages/{id}/replies

A thread: its root (with its reply count) and its replies, newest first, each with its author's current display name and the message it answered (quote). Page back with before. The id of a reply names its thread too. The answer names the thread's topic: a link to a message (/chat/<topic>?m=<id>) finds its page and thread from it.

Path parameters #

ParameterTypeRequiredDescription
idintegeryesThe thread's root (or one of its replies).

Query parameters #

ParameterTypeRequiredDescription
beforeintegernoA reply id: only older ones (the previous page's next). (min 1)
limitintegernoReplies per page, 1-100. Default: 50. (min 1, max 100)

Example request #

cURL
curl 'https://raresats.net/api/chat/messages/1042/replies?limit=50'
JavaScript
const response = await fetch('https://raresats.net/api/chat/messages/1042/replies?limit=50');
const data = await response.json();
Python
import requests

data = requests.get("https://raresats.net/api/chat/messages/1042/replies", params={"limit": 50}).json()

Response 200 #

FieldTypeDescription
topicstringThe thread's topic (its root's): a link to a reply finds its page.
roomstringDeprecated: the same as topic.
rootobjectA message, or a reply in a thread (one level: a reply to a reply joins its root's thread).
root.idintegerIncreasing; the paging cursor.
root.topicstringThe topic it is in (a reply: its root's).
root.roomstringDeprecated: the same as topic (the one room before topics was main, now general).
root.addressstringThe author.
root.display_namestring | nullThe author's display name now.
root.adminbooleanThe author is a chat admin.
root.textstringPlain text (NFC, no control, zero-width or bidi characters, one line, 1-500 characters). Show it as text, never as HTML.
root.created_atintegerUnix seconds.
root.reply_tointeger | nullThe thread's root, on a reply; null on a root (a message of the topic).
root.reply_countintegerOn a root: its live replies (0 on a reply).
root.last_reply_atinteger | nullOn a root: its newest reply's time, unix seconds; null without replies.
root.quoteobject | nullOn a reply: the message it answered (the root, or a reply in the thread), while it is live; text only, cut at 140 characters. Null on a root.
root.quote.idinteger
root.quote.addressstring
root.quote.display_namestring | null
root.quote.textstring
messagesarray of objectThe replies, newest first.
messages[].idintegerIncreasing; the paging cursor.
messages[].topicstringThe topic it is in (a reply: its root's).
messages[].roomstringDeprecated: the same as topic (the one room before topics was main, now general).
messages[].addressstringThe author.
messages[].display_namestring | nullThe author's display name now.
messages[].adminbooleanThe author is a chat admin.
messages[].textstringPlain text (NFC, no control, zero-width or bidi characters, one line, 1-500 characters). Show it as text, never as HTML.
messages[].created_atintegerUnix seconds.
messages[].reply_tointeger | nullThe thread's root, on a reply; null on a root (a message of the topic).
messages[].reply_countintegerOn a root: its live replies (0 on a reply).
messages[].last_reply_atinteger | nullOn a root: its newest reply's time, unix seconds; null without replies.
messages[].quoteobject | nullOn a reply: the message it answered (the root, or a reply in the thread), while it is live; text only, cut at 140 characters. Null on a root.
messages[].quote.idinteger
messages[].quote.addressstring
messages[].quote.display_namestring | null
messages[].quote.textstring
nextinteger | nullPass as before for older replies; null at the start.
Response
{
  "topic": "market",
  "room": "market",
  "root": {
    "id": 1042,
    "topic": "market",
    "room": "market",
    "address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
    "display_name": "Rare Hunter",
    "admin": false,
    "text": "gm, any block 9 for sale?",
    "created_at": 1790949400,
    "reply_to": null,
    "reply_count": 2,
    "last_reply_at": 1790949460,
    "quote": null
  },
  "messages": [
    {
      "id": 1044,
      "topic": "market",
      "room": "market",
      "address": "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq",
      "display_name": null,
      "admin": false,
      "text": "I have two, 0.0021 each",
      "created_at": 1790949460,
      "reply_to": 1042,
      "reply_count": 0,
      "last_reply_at": null,
      "quote": {
        "id": 1042,
        "address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
        "display_name": "Rare Hunter",
        "text": "gm, any block 9 for sale?"
      }
    }
  ],
  "next": null
}

Errors #

{error, code}; see Errors for every code.

StatusCodeMeaning
400bad_paginglimit or offset out of range.
400bad_pathThe path is not valid percent-encoding.
404chat_disabledChat and display names are off on this server.
404not_foundNo such listing or offer. In chat: no such message. A display name to remove that does not exist.
405method_not_allowedWrong method for the route; the allow header lists the right ones.

Post a message #

POST/api/chat/messages

Posts as the session's address (the __Host-rs_chat cookie of Sign in), in a topic (topic, default general) or, with reply_to, as a reply in its root's topic: one level of threads, so a reply to a reply joins its root's thread (and quotes the reply it answered). Plain text: normalized to NFC, control, zero-width and bidi characters dropped, line breaks made spaces; 1-500 characters. One message every 3 s and 20 a minute per address; the same text twice within 2 minutes is refused; the whole chat takes at most 5 messages a second (429 room_busy). A link must name a plain ASCII host (422 suspicious_link). Then, where the AI moderation is on (the session's moderated), the text is checked before it is posted: refused, 422 moderated with its category; when it cannot be checked, 503 moderation_unavailable and nothing is posted (try again); 3 refusals in 10 minutes mute the address for an hour, and 6 from one IPv4 address or IPv6 /48 within an hour pause that network (403 muted); at most 6 texts a minute are checked per address and per network (429 rate_limited). Delivered live as a chat event on /api/events, which names the topic; a reply's answer and event carry its root's new thread counts.

Request body #

content-type: application/json

FieldTypeRequiredDescription
textstringyesThe message.
topicstring | nullnoThe topic's id; absent or null: general. With reply_to it may be left out; given, it must be the root's topic.
reply_tointeger | nullnoReply to this message (a root, or a reply: the reply joins its root's thread, in its topic). Absent or null: a new message in the topic.

Example request #

cURL
curl -X POST 'https://raresats.net/api/chat/messages' \
  -H 'content-type: application/json' \
  -d '{"text": "I have two, 0.0021 each", "topic": "market", "reply_to": 1042}'
JavaScript
const response = await fetch('https://raresats.net/api/chat/messages', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({"text": "I have two, 0.0021 each", "topic": "market", "reply_to": 1042})
});
const data = await response.json();
Python
import requests

data = requests.post("https://raresats.net/api/chat/messages", json={
    "text": "I have two, 0.0021 each",
    "topic": "market",
    "reply_to": 1042
}).json()

Response 200 #

FieldTypeDescription
messageobjectA message, or a reply in a thread (one level: a reply to a reply joins its root's thread).
message.idintegerIncreasing; the paging cursor.
message.topicstringThe topic it is in (a reply: its root's).
message.roomstringDeprecated: the same as topic (the one room before topics was main, now general).
message.addressstringThe author.
message.display_namestring | nullThe author's display name now.
message.adminbooleanThe author is a chat admin.
message.textstringPlain text (NFC, no control, zero-width or bidi characters, one line, 1-500 characters). Show it as text, never as HTML.
message.created_atintegerUnix seconds.
message.reply_tointeger | nullThe thread's root, on a reply; null on a root (a message of the topic).
message.reply_countintegerOn a root: its live replies (0 on a reply).
message.last_reply_atinteger | nullOn a root: its newest reply's time, unix seconds; null without replies.
message.quoteobject | nullOn a reply: the message it answered (the root, or a reply in the thread), while it is live; text only, cut at 140 characters. Null on a root.
message.quote.idinteger
message.quote.addressstring
message.quote.display_namestring | null
message.quote.textstring
threadobjectOn a reply: its root's thread now.
thread.idintegerThe root.
thread.reply_countinteger
thread.last_reply_atinteger | null

Errors #

{error, code}; see Errors for every code.

StatusCodeMeaning
400bad_jsonThe body is not valid JSON.
400bad_requestThe body is not a JSON object, or the engine refused a malformed request (e.g. a purchase submitted without its signed setup PSBT).
400bad_topicA topic id that is not one: 1-32 of a-z, 0-9 and inner hyphens (and, to create one, not main). On a reply: a topic other than its root's.
401no_sessionNo chat session: sign in (POST /api/session) and send its cookie.
403bannedThe address is banned from chat: it may not post or set a display name.
403cross_originA POST from a browser on another site (a foreign Origin, or sec-fetch-site: cross-site). Server-side clients send no Origin and are not affected.
403mutedThe address is muted (by the AI moderation after repeated refusals: 3 in 10 minutes), or its network (an IPv4 address or IPv6 /48) is paused after 6 refusals within an hour, until until, unix seconds: it may not post or set a display name until then.
404chat_disabledChat and display names are off on this server.
404not_foundNo such listing or offer. In chat: no such message. A display name to remove that does not exist.
404unknown_topicNo such chat topic, or an archived one (which takes no new messages).
405method_not_allowedWrong method for the route; the allow header lists the right ones.
409duplicateThe same message (ignoring case) from the same address within 2 minutes.
413too_largeThe body is over 256 KiB.
415not_jsonA POST without content-type: application/json.
422empty_textNothing is left of the message once control, zero-width and bidi characters are dropped and spaces collapsed.
422moderatedThe AI moderation refused the chat message or display name; category says why (the message is not posted, the name not taken). Three refusals in 10 minutes mute the address for an hour (until).
422suspicious_linkA link in the message names a host that is not plain ASCII: look-alike letters from other scripts, punycode (xn--), or user@host. Write the link with its real host name.
422text_too_longA chat message is at most 500 characters.
429chat_busyToo many chat messages and names are being checked across the site right now (over the global cap of model calls a minute); wait retry-after seconds.
429rate_limitedOver 30 POSTs a minute from your IPv4 address or IPv6 /64 (in chat, counted per IPv4 address or IPv6 /64: 30 writes and 20 login nonces a minute, 20 sign-ins and 10 display-name changes an hour; one message every 3 s and 20 a minute per address); wait retry-after seconds. With the AI moderation on, at most 6 messages and names a minute are checked per address and per IPv4 address or IPv6 /48.
429room_busyOver 5 chat messages a second across the whole chat; wait retry-after seconds.
503moderation_unavailableThe AI moderation could not check the text right now (timeout or model error): nothing was posted. Try again in a moment.
Error (503)
{
  "error": "Couldn't check your message right now: try again in a moment",
  "code": "moderation_unavailable"
}

Delete a message (admin) #

DELETE/api/chat/messages/{id}

An admin session deletes a message. Deleting a thread's root deletes its replies too (deleted lists them all). Pages are told with a chat event {type: "delete", ids, threads, topics}. Logged.

Path parameters #

ParameterTypeRequiredDescription
idintegeryesThe message id.

Example request #

cURL
curl -X DELETE 'https://raresats.net/api/chat/messages/1042' -H 'cookie: __Host-rs_chat=<token>'
JavaScript
const response = await fetch('https://raresats.net/api/chat/messages/1042', { method: 'DELETE', credentials: 'include' });
const data = await response.json();

Response 200 #

FieldTypeDescription
deletedarray of integer

Errors #

{error, code}; see Errors for every code.

StatusCodeMeaning
400bad_pathThe path is not valid percent-encoding.
401no_sessionNo chat session: sign in (POST /api/session) and send its cookie.
403cross_originA POST from a browser on another site (a foreign Origin, or sec-fetch-site: cross-site). Server-side clients send no Origin and are not affected.
403not_adminOnly a chat admin (SUPPLY_CHAT_ADMINS) may do that.
404chat_disabledChat and display names are off on this server.
404not_foundNo such listing or offer. In chat: no such message. A display name to remove that does not exist.
405method_not_allowedWrong method for the route; the allow header lists the right ones.
429rate_limitedOver 30 POSTs a minute from your IPv4 address or IPv6 /64 (in chat, counted per IPv4 address or IPv6 /64: 30 writes and 20 login nonces a minute, 20 sign-ins and 10 display-name changes an hour; one message every 3 s and 20 a minute per address); wait retry-after seconds. With the AI moderation on, at most 6 messages and names a minute are checked per address and per IPv4 address or IPv6 /48.

List bans (admin) #

GET/api/chat/bans

The banned addresses, newest first. An admin session only.

Example request #

cURL
curl 'https://raresats.net/api/chat/bans'
JavaScript
const response = await fetch('https://raresats.net/api/chat/bans');
const data = await response.json();
Python
import requests

data = requests.get("https://raresats.net/api/chat/bans").json()

Response 200 #

FieldTypeDescription
bansarray of object
bans[].addressstring
bans[].reasonstring | null
bans[].banned_bystringThe admin's address, or moderation for an automatic mute.
bans[].created_atinteger
bans[].expires_atinteger | nullWhen a mute ends, unix seconds; null: banned until unbanned.

Errors #

{error, code}; see Errors for every code.

StatusCodeMeaning
401no_sessionNo chat session: sign in (POST /api/session) and send its cookie.
403not_adminOnly a chat admin (SUPPLY_CHAT_ADMINS) may do that.
404chat_disabledChat and display names are off on this server.
405method_not_allowedWrong method for the route; the allow header lists the right ones.

Ban or unban (admin) #

POST/api/chat/bans

An admin session bans (banned: true) or unbans an address. A banned address cannot post or set a display name, and a ban removes its display name for good. delete_recent also deletes its messages of the last 24 hours. Logged.

Request body #

content-type: application/json

FieldTypeRequiredDescription
addressstringyes
bannedbooleanyes
reasonstring | nullnoAt most 200 characters.
delete_recentbooleannoAlso delete its messages of the last 24 hours.

Example request #

cURL
curl -X POST 'https://raresats.net/api/chat/bans' \
  -H 'content-type: application/json' \
  -d '{
  "address": "bc1qm34lsc65zpw79lxes69zkqmk6ee3ewf0j77s3h",
  "banned": true,
  "reason": "spam",
  "delete_recent": true
}'
JavaScript
const response = await fetch('https://raresats.net/api/chat/bans', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    "address": "bc1qm34lsc65zpw79lxes69zkqmk6ee3ewf0j77s3h",
    "banned": true,
    "reason": "spam",
    "delete_recent": true
  })
});
const data = await response.json();
Python
import requests

data = requests.post("https://raresats.net/api/chat/bans", json={
    "address": "bc1qm34lsc65zpw79lxes69zkqmk6ee3ewf0j77s3h",
    "banned": True,
    "reason": "spam",
    "delete_recent": True
}).json()

Response 200 #

FieldTypeDescription
addressstring
bannedboolean
banobject | null
deletedarray of integerThe messages deleted with it.

Errors #

{error, code}; see Errors for every code.

StatusCodeMeaning
400bad_addressAn address is not a mainnet address (or not one at all).
400bad_jsonThe body is not valid JSON.
400bad_requestThe body is not a JSON object, or the engine refused a malformed request (e.g. a purchase submitted without its signed setup PSBT).
401no_sessionNo chat session: sign in (POST /api/session) and send its cookie.
403cross_originA POST from a browser on another site (a foreign Origin, or sec-fetch-site: cross-site). Server-side clients send no Origin and are not affected.
403not_adminOnly a chat admin (SUPPLY_CHAT_ADMINS) may do that.
404chat_disabledChat and display names are off on this server.
405method_not_allowedWrong method for the route; the allow header lists the right ones.
413too_largeThe body is over 256 KiB.
415not_jsonA POST without content-type: application/json.
422not_bannableAn admin cannot be banned.
429rate_limitedOver 30 POSTs a minute from your IPv4 address or IPv6 /64 (in chat, counted per IPv4 address or IPv6 /64: 30 writes and 20 login nonces a minute, 20 sign-ins and 10 display-name changes an hour; one message every 3 s and 20 a minute per address); wait retry-after seconds. With the AI moderation on, at most 6 messages and names a minute are checked per address and per IPv4 address or IPv6 /48.