freeq exposes a REST API alongside the IRC and WebSocket interfaces.
https://irc.freeq.at/api/v1
GET /api/v1/health
Returns server status:
{
"server_name": "irc.freeq.at",
"connections": 42,
"channels": 12,
"uptime_secs": 86400
}
GET /api/v1/channels
Returns public channels with member counts:
[
{
"name": "#freeq",
"topic": "Welcome to freeq",
"members": 15,
"modes": "+nt"
}
]
Filters out empty channels with no topic.
GET /api/v1/history/{channel}?limit=50&before={msgid}
Returns recent messages. Requires the channel name without # prefix.
GET /api/v1/verify/{msgid}
Verify a message's cryptographic signature. Returns the signing key, signature, and verification result.
GET /api/v1/signing-key
Returns the server's ed25519 public key (base64url-encoded) used for message attestation.
GET /api/v1/blob?url={encoded-pds-url}&mime={encoded-mime}
Proxies PDS blob downloads. Strips Content-Disposition: attachment headers that block browser playback. Supports Range requests for streaming.
GET /api/v1/og?url={encoded-url}
Fetches Open Graph metadata for a URL. Returns title, description, image, and site name. Server-side fetch prevents IP leakage.
POST /api/v1/upload
Authorization: Bearer {web-token}
Content-Type: multipart/form-data
Upload a file to the user's PDS. Returns the blob URL and media attachment tags.
GET /api/v1/pins/{channel}
Returns pinned messages for a channel:
[
{
"msgid": "01ABCDEF...",
"from": "alice",
"text": "Welcome!",
"pinned_by": "bob",
"pinned_at": "2024-01-01T00:00:00Z"
}
]
Most read endpoints are public. Write endpoints (upload, pin) require a web-token from the auth broker, sent as Authorization: Bearer {token}.
Allowed origins: irc.freeq.at, auth.freeq.at, freeq.at, localhost:*.
All responses include:
- Content-Security-Policy (strict)
- Strict-Transport-Security (HSTS)
- X-Frame-Options: DENY
- X-Content-Type-Options: nosniff
- Referrer-Policy: strict-origin-when-cross-origin