raresats.net API
Chat & profiles API View as Markdown

Chat sessions

Reading chat is public. Posting needs a session, opened by one BIP-322 signature: the walkthrough is in the Chat & profiles guide.

Get a login nonce #

GET/api/session/nonce

A nonce for an address to sign in with: usable once, for 10 minutes, by that address only. Same-origin, and limited to 20 a minute per IPv4 address or IPv6 /64.

Query parameters #

ParameterTypeRequiredDescription
addressstringyesThe address that will sign.

Example request #

cURL
curl 'https://raresats.net/api/session/nonce?address=bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh'
JavaScript
const response = await fetch('https://raresats.net/api/session/nonce?address=bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh');
const data = await response.json();
Python
import requests

data = requests.get("https://raresats.net/api/session/nonce", params={"address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh"}).json()

Response 200 #

FieldTypeDescription
addressstringThe address, normalized.
noncestring32 hex characters, usable once.
expires_atintegerWhen the nonce expires, unix seconds (10 minutes).
messagestringThe message to sign, with <unix seconds> for the current time.
Response
{
  "address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
  "nonce": "9f2c41d07a5be3816c0d4f27e9a1b358",
  "expires_at": 1790950000,
  "message": "raresats.net login bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh 9f2c41d07a5be3816c0d4f27e9a1b358 <unix seconds>"
}

Errors #

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

StatusCodeMeaning
400bad_addressAn address is not a mainnet address (or not one at all).
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.
404chat_disabledChat and display names are off on this server.
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.

Sign in to chat #

POST/api/session

Opens a chat session from a BIP-322 signature of raresats.net login <address> <nonce> <unix seconds>, the nonce from Get a login nonce. Sets the session cookie __Host-rs_chat (HttpOnly, Secure, SameSite=Lax, Path=/, 30 days). The nonce and the message are each usable once; the message at most 10 minutes old. At most 20 sign-ins an hour per IPv4 address or IPv6 /64.

Request body #

content-type: application/json

FieldTypeRequiredDescription
addressstringyesThe address signing in (ordinals or payment).
messagestringyesraresats.net login <address> <nonce> <unix seconds>, at most 10 minutes old.
signaturestringyesBIP-322 signature of message by address (base64; simple or full).

Example request #

cURL
curl -X POST 'https://raresats.net/api/session' \
  -H 'content-type: application/json' \
  -d '{
  "address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
  "message": "raresats.net login bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh 9f2c41d07a5be3816c0d4f27e9a1b358 1790949400",
  "signature": "AUBGQvdc2H+7HTo1njZ1vlDxoX6ncoehAGbaHcAY1FtfSNg7lmo8Ih3WGInhAH/snOUIb8DkwRSQxc2dOu911Z9E"
}'
JavaScript
const response = await fetch('https://raresats.net/api/session', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    "address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
    "message": "raresats.net login bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh 9f2c41d07a5be3816c0d4f27e9a1b358 1790949400",
    "signature": "AUBGQvdc2H+7HTo1njZ1vlDxoX6ncoehAGbaHcAY1FtfSNg7lmo8Ih3WGInhAH/snOUIb8DkwRSQxc2dOu911Z9E"
  })
});
const data = await response.json();
Python
import requests

data = requests.post("https://raresats.net/api/session", json={
    "address": "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
    "message": "raresats.net login bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh 9f2c41d07a5be3816c0d4f27e9a1b358 1790949400",
    "signature": "AUBGQvdc2H+7HTo1njZ1vlDxoX6ncoehAGbaHcAY1FtfSNg7lmo8Ih3WGInhAH/snOUIb8DkwRSQxc2dOu911Z9E"
}).json()

Response 200 #

FieldTypeDescription
enabledbooleanChat and display names are on (always true: off, the route is a 404).
max_lengthintegerThe longest message, in characters.
addressstring | nullThe signed-in address.
adminbooleanThe address is a chat admin.
display_namestring | nullIts display name.
expires_atinteger | nullWhen the session ends, unix seconds.
bannedbooleanThe address may not post: banned, or muted until banned_until.
server_timeintegerThe server's clock, unix seconds: date signed messages by it.
banned_untilinteger | nullWhen a mute ends, unix seconds (the AI moderation mutes an address for an hour after 3 refusals in 10 minutes); null when not muted or banned for good.
moderatedbooleanMessages and display names are checked by the AI moderation before they are posted.

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_messageThe cancel message is not raresats.net cancel <ids> <unix seconds> or names other listings than ids; or the registration message is not raresats.net affiliate <address> <unix seconds> for address.
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).
400, 422bad_signature400: signature is missing or too long. 422: a signature does not verify (cancel or registration message, listing or purchase input).
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.
404chat_disabledChat and display names are off on this server.
405method_not_allowedWrong method for the route; the allow header lists the right ones.
409replayedThat cancel or registration message was used already.
413too_largeThe body is over 256 KiB.
415not_jsonA POST without content-type: application/json.
422bad_nonceThe login nonce was not handed out for this address, has expired (10 minutes) or was used: ask for a new one.
422future_messageThe cancel or registration message is dated more than 2 minutes ahead: check the clock.
422stale_messageThe cancel or registration message is more than 10 minutes old: sign a new one.
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.
Error (422)
{
  "error": "that nonce was not handed out for this address, has expired or was used: ask for a new one",
  "code": "bad_nonce"
}

Get the session #

GET/api/session

The session of the request's __Host-rs_chat cookie: the signed-in address, its name and whether it is an admin or banned; address null when signed out. A browser's page uses it to know whether chat is on (off: 404 chat_disabled).

Example request #

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

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

Response 200 #

FieldTypeDescription
enabledbooleanChat and display names are on (always true: off, the route is a 404).
max_lengthintegerThe longest message, in characters.
addressstring | nullThe signed-in address.
adminbooleanThe address is a chat admin.
display_namestring | nullIts display name.
expires_atinteger | nullWhen the session ends, unix seconds.
bannedbooleanThe address may not post: banned, or muted until banned_until.
server_timeintegerThe server's clock, unix seconds: date signed messages by it.
banned_untilinteger | nullWhen a mute ends, unix seconds (the AI moderation mutes an address for an hour after 3 refusals in 10 minutes); null when not muted or banned for good.
moderatedbooleanMessages and display names are checked by the AI moderation before they are posted.

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.

Sign out #

DELETE/api/session

Ends the cookie's session and clears the cookie. Answers the signed-out session.

Example request #

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

Response 200 #

FieldTypeDescription
enabledbooleanChat and display names are on (always true: off, the route is a 404).
max_lengthintegerThe longest message, in characters.
addressstring | nullThe signed-in address.
adminbooleanThe address is a chat admin.
display_namestring | nullIts display name.
expires_atinteger | nullWhen the session ends, unix seconds.
bannedbooleanThe address may not post: banned, or muted until banned_until.
server_timeintegerThe server's clock, unix seconds: date signed messages by it.
banned_untilinteger | nullWhen a mute ends, unix seconds (the AI moderation mutes an address for an hour after 3 refusals in 10 minutes); null when not muted or banned for good.
moderatedbooleanMessages and display names are checked by the AI moderation before they are posted.

Errors #

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

StatusCodeMeaning
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.
404chat_disabledChat and display names are off on this server.
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.