QERYX COMING SOON
OBSERVATORY

Docs · Reference

Read the relay field by field.

Every request the clients make, with the exact shapes the server reads — and, field by field, what it can never read.

Point at the base URL

every path below is relative to the relay's /api/v1 base
Authorization: Bearer <jwt>   # on every authenticated request

This reference documents the surface the clients speak, mirrored from the server's own request structures. A field that is not listed is not read — the server rejects unknown fields on credential payloads rather than silently accepting them.

Authenticate

An account is a derived Profile ID and an account secret. Those two fields, plus the connecting address the relay sees to answer at all, are the identity surface — there is no name, no number, and no address book.

POST /auth/register Create an account: a derived Profile ID and an account secret.
POST /auth/login Authenticate; returns a bearer token.
POST /auth/refresh Rotate the token before it expires.
POST /auth/logout End the server session.
auth/register
{
  "serpent_id": "string   — 3–64 chars: letters, digits, - and _",
  "password":   "string   — 8–128 chars"
}

// 200 —
{
  "token":      "<jwt>",
  "user_id":    "<uuid — internal routing id>",
  "profile_id": "PROFILE-XXXXXXXX-XXXXXXXX"
}

The relay stores the sign-in secret as an Argon2id hash (relay m=64 MiB, t=3, p=4 — RFC 9106, with a rolling rehash on sign-in). The Profile ID is derived on your device; the server stores it verbatim and routes by it.

Publish and fetch keys

Everything under /keys is public material. The relay is a bulletin board for public keys; private keys never appear in any payload on this page.

POST /keys/bundle Publish your public key bundle: identity, ML-KEM-1024, X25519, signed prekeys.
GET /keys/bundle/by-profile/:profile_id Fetch a peer's public bundle by Profile ID.
POST /keys/signing Publish your ML-DSA-87 signing public key.
GET /keys/signing/by-profile/:profile_id Fetch a peer's signing public key.

Bundles are signed (the signed-prekey binding closes the classic substitution attack), identity keys are appended to the KTS transparency log, and the safety-number ritual in Key verification lets two humans confirm the relay served the same keys to both sides.

Open a session

A session opens with one round trip: init carries the hybrid handshake, respond completes it. The server validates structure and bindings; the secrets are born on the two devices.

POST /sessions/init Open the hybrid handshake with a peer.
POST /sessions/respond Answer an initiated handshake.
GET /sessions/:peer_id Read the session state shared with one peer.
sessions/init
{
  "responder_id":         "<uuid>",
  "handshake_init":       "<base64 — serialized hybrid HandshakeInit>",
  "bell_transcript":      "<base64 — canonical Q-Entangle-Bell v1 bytes>",
  "bell_transcript_hash": "<base64 — SHA3-256 of the canonical bytes>",
  "bell_op_sig":          "<base64 — operator signature over the transcript; optional>"
}

The Bell fields are optional. When present, the server recomputes SHA3-256 over the transcript bytes and re-checks the CHSH window; a mismatch is counted and, once enforcement is armed on the deployment, refused with X-Qeryx-Bell-Missing: 1. The messenger bootstraps its sessions through the pre-key bundle path, not this endpoint.

This is not QKD. Not quantum networking. Not quantum teleportation. We do not violate the no-communication theorem. Above an X25519 + ML-KEM-1024 floor that alone provides confidentiality, a session can fold a Tsirelson-bounded CHSH witness transcript into its key derivation — a deterministic classical co-possession witness, not physical entanglement; security never depends on it.

Send a message

The envelope the relay carries is ciphertext plus routing. Group names and descriptions and all content are end-to-end encrypted; the group avatar, the admin and owner identifiers, and the roster of accounts each conversation delivers to are held by the relay for routing. Sender identifiers are stripped from the stored message row (sealed sender), though the relay authenticates the sender at send time and keeps a per-direction pair counter for up to 180 days. Sealed sender covers text-class messages and small payloads: an image, and any voice or video frame over the envelope budget, is delivered on a lane that does name the sender to the relay for as long as the row lives. Undelivered messages queue with routing metadata under a published retention ladder.

POST /messages Relay one ciphertext envelope.
GET /messages Drain queued envelopes. Query: since=<ISO 8601>.
POST /messages/sealed Sealed-sender envelope, wire version 1 — the stored row carries no sender.
POST /messages/receipts Post delivery / read receipts.
DELETE /messages/:message_id Delete an envelope.
messages
{
  "recipient_id":      "PROFILE-… or <uuid>",
  "encrypted_content": "<base64 — ciphertext; opaque to the relay>",
  "nonce":             "<base64>",
  "message_type":      "text | image | video | file | location | voice",
  "reply_to_id":       "<uuid, optional>",
  "conversation_id":   "<string, optional — group routing>",
  "idempotency_key":   "<string, optional — client-side dedup>"
}

encrypted_content and nonce are opaque bytes to the relay. message_type is a routing hint for notifications; the content behind it is indistinguishable ciphertext.

Audit the transparency log

The endpoints an auditor uses. Inclusion proofs, auditor keys, signed audit receipts, and the build attestation are readable without any special access — verification is a public capability, not a support ticket.

POST /kts/append Append an identity key to the transparency log.
GET /kts/proof/by-uuid/:uuid Fetch an inclusion proof for a logged key.
GET /kts/auditors List the enrolled cosigning keys — the operator at first, external auditors when they enroll.
GET /fsor/audit Fetch a signed audit receipt.
GET /fsor/audit/latest-batch Read the latest public audit batch.
GET /attestation/current Read the build attestation, once a release provisions one.

KTS is an append-only key log anchored by a 2-of-3 multi-witness quorum that starts dormant — all three ML-DSA-87 slots and all three external Ed25519 auditor slots hold placeholder keys, so no cosignature is carried and no witness check runs yet. Every root, until the witnesses enroll, is signed by one operator ML-DSA-87 key, which your device verifies against a SHA-384 anchor compiled into the app and cross-checks peer-to-peer through gossiped log roots. Enrolling a witness takes a new app release, by design.

Hold the socket open

realtime delivery
GET /ws
Authorization: Bearer <jwt>

# ?token=<jwt> is accepted as a fallback and deprecated —
# tokens in URLs end up in logs.

One socket carries typed frames, dispatched on msg_type: delivery (delivered, delivery_ack, read), call signaling (call_ready, call_accept, call_reject, call_end and their video variants), group media control, and transparency gossip. Message payloads inside frames are the same opaque ciphertext as the REST surface.

Push on the reference.

Is there a third-party app program or API keys?

No. This reference exists so you can verify what the clients send — and what the relay can read — not to onboard bots. There are no bots and no server-side AI in the messenger core, by design. The Qaduceus assistant is a separate opt-in surface with its own documented path.

Because the API surface is the privacy claim. Anyone can read these shapes and, once QERYX arrives, capture their own traffic and confirm the two match. A privacy policy asks for trust; a field list can be checked.

The relay enforces per-route rate limits (auth endpoints are the strictest). Exact thresholds are operational tuning and move with load; the error you get is HTTP 429 with a retry hint.