Q&A Web API Documentation
The Q&A Web API provides REST endpoints for the community Q&A system. It enables web interfaces to browse questions, submit answers, vote, and view leaderboards.
Base URL: https://signal.irregulars.io (bot API) or https://qa.irregulars.io (web form)
Authentication
Section titled “Authentication”API Key (Bot API)
Section titled “API Key (Bot API)”For protected endpoints, include the API key in the header:
X-Api-Key: <your-api-key>OIDC (Web Form)
Section titled “OIDC (Web Form)”The Q&A web form at qa.irregulars.io uses OIDC authentication via Authentik:
- User clicks “Login”
- Redirected to Authentik SSO
- After authentication, redirected back with session cookie
- Session stored in Cloudflare KV
Questions API
Section titled “Questions API”GET /questions
Section titled “GET /questions”List questions with filtering and pagination.
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
query | string | - | Text search in question content |
status | string | open | Filter: open, solved, needs_answer, all |
sort | string | newest | Sort: newest, oldest, most_answers, unanswered |
limit | number | 50 | Results per page (max: 100) |
offset | number | 0 | Pagination offset |
Response
Section titled “Response”{ "success": true, "questions": [ { "id": "q-1737900123456-abc123", "question_id": 42, "question": "How do I configure two-factor authentication?", "asker": "Alice", "asker_uuid": "uuid-...", "group_id": "abc123...", "group_name": "IrregularChat: Security", "solved": false, "answer_count": 2, "view_count": 15, "timestamp": "2026-01-25T10:30:00Z", "created_at": "2026-01-25T10:30:00Z" } ], "total": 150, "offset": 0, "limit": 50, "hasMore": true}GET /questions/:questionId
Section titled “GET /questions/:questionId”Get a single question with full context including answers, clarifications, and metadata.
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Description |
|---|---|---|
group_id | string | Filter to specific group (optional) |
Response
Section titled “Response”{ "success": true, "question": { "id": "q-1737900123456-abc123", "question_id": 42, "question": "How do I configure two-factor authentication?", "asker": "Alice", "asker_uuid": "uuid-...", "group_id": "abc123...", "group_name": "IrregularChat: Security", "solved": true, "answer_count": 2, "view_count": 15, "timestamp": "2026-01-25T10:30:00Z" }, "answers": [ { "answer_id": 1, "answer": "Go to Settings > Security > Enable 2FA...", "answerer": "Bob", "answerer_uuid": "uuid-...", "is_solution": true, "status": "accepted", "upvotes": 5, "downvotes": 0, "sources": [ {"url": "https://docs.example.com/2fa"} ], "confidence_level": 90, "created_at": "2026-01-25T11:00:00Z" } ], "clarifications": [ { "clarification_id": 1, "clarification": "Are you asking about app-based or hardware 2FA?", "author": "Carol", "created_at": "2026-01-25T10:45:00Z" } ], "resources": { "forum_url": "https://forum.irregularchat.com/t/123", "breakout_summary": "Discussion concluded with recommendation for TOTP apps" }}POST /questions/:questionId/view
Section titled “POST /questions/:questionId/view”Record a question view (for analytics).
Request Body
Section titled “Request Body”{ "viewerUuid": "uuid-...", "viewerName": "Alice", "source": "web"}Response
Section titled “Response”{ "success": true}POST /questions/:questionId/answers
Section titled “POST /questions/:questionId/answers”Submit an answer to a question.
Request Body
Section titled “Request Body”{ "answer": "Here's how to set up 2FA...", "answererName": "Bob", "answererUuid": "uuid-...", "groupId": "abc123...", "sources": [ {"url": "https://docs.example.com/2fa"} ], "confidenceLevel": 85}| Field | Required | Description |
|---|---|---|
answer | Yes | The answer text |
answererName | Yes | Display name of answerer |
answererUuid | No | UUID for tracking (from OIDC) |
groupId | Yes | Group ID where question was asked |
sources | No | Array of source URLs |
confidenceLevel | No | 0-100 confidence score |
Response
Section titled “Response”{ "success": true, "answerId": 5, "message": "Answer submitted successfully", "notified": ["Alice", "Carol"]}Side Effects:
- DM notification sent to question asker (if bot is running)
- DM notification sent to any users who added clarifications
POST /questions/:questionId/answers/:answerId/vote
Section titled “POST /questions/:questionId/answers/:answerId/vote”Vote on an answer (upvote or downvote).
Request Body
Section titled “Request Body”{ "voteType": "up", "voterUuid": "uuid-...", "voterName": "Carol", "groupId": "abc123..."}| Field | Required | Description |
|---|---|---|
voteType | Yes | up or down |
voterUuid | Yes | UUID of voter |
voterName | No | Display name |
groupId | Yes | Group context |
Response
Section titled “Response”{ "success": true, "upvotes": 6, "downvotes": 1, "score": 5}Rules:
- One vote per user per answer
- Can change vote (up to down or vice versa)
- Cannot vote on your own answer
PUT /questions/:questionId/answers/:answerId
Section titled “PUT /questions/:questionId/answers/:answerId”Edit an existing answer (only by original answerer).
Request Body
Section titled “Request Body”{ "answer": "Updated answer text...", "editorUuid": "uuid-...", "editorName": "Bob", "groupId": "abc123..."}Response
Section titled “Response”{ "success": true}Restrictions:
- Only the original answerer can edit
- Returns 403 if
editorUuiddoesn’t matchanswerer_uuid
POST /questions/:questionId/clarifications
Section titled “POST /questions/:questionId/clarifications”Add a clarification or follow-up to a question.
Request Body
Section titled “Request Body”{ "clarification": "Are you asking about app-based or hardware 2FA?", "authorUuid": "uuid-...", "authorName": "Carol", "groupId": "abc123..."}Response
Section titled “Response”{ "success": true, "clarificationId": 3}GET /questions/:questionId/answers/:answerId/votes
Section titled “GET /questions/:questionId/answers/:answerId/votes”Get vote details for a specific answer.
Response
Section titled “Response”{ "success": true, "upvotes": 6, "downvotes": 1, "voters": [ {"uuid": "uuid-...", "name": "Alice", "vote": "up"}, {"uuid": "uuid-...", "name": "Dave", "vote": "down"} ]}Leaderboard API
Section titled “Leaderboard API”GET /leaderboard
Section titled “GET /leaderboard”Get community leaderboard ranked by helpful answers.
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
period | string | all | Time period: all, month, week |
limit | number | 50 | Number of users to return |
Response
Section titled “Response”{ "success": true, "period": "all", "updated_at": "2026-01-26T12:00:00Z", "leaderboard": [ { "rank": 1, "uuid": "uuid-...", "username": "HelpfulBob", "score": 150, "solutions_count": 5, "total_answers": 20, "acceptance_rate": 25, "total_upvotes": 45, "total_downvotes": 2, "first_answer_at": "2025-06-15T10:00:00Z", "last_answer_at": "2026-01-25T15:00:00Z", "badges": ["Top Contributor", "First Place"] } ], "total": 42}Scoring Formula
Section titled “Scoring Formula”| Action | Points |
|---|---|
| Solution accepted | +25 |
| Upvote received | +2 |
| Downvote received | -1 |
Exclusions:
- Self-answers (answering your own question) don’t count
- Rejected answers don’t count
Badges
Section titled “Badges”| Badge | Criteria |
|---|---|
| Top Contributor | 10+ solutions |
| First Place | Rank #1 |
| Second Place | Rank #2 |
| Third Place | Rank #3 |
| Rising Star | 5+ solutions |
| Helper | 1+ solutions |
GET /leaderboard/user/:uuid
Section titled “GET /leaderboard/user/:uuid”Get a specific user’s leaderboard position.
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
period | string | all | Time period filter |
Response
Section titled “Response”{ "success": true, "user_rank": { "rank": 5, "uuid": "uuid-...", "username": "Alice", "score": 75, "solutions_count": 3, "total_answers": 12, "badges": ["Rising Star"] }}If user has no qualifying answers:
{ "success": true, "user_rank": null, "message": "User not found on leaderboard (no qualifying answers)"}User Activity API
Section titled “User Activity API”GET /users/:uuid/qa-activity
Section titled “GET /users/:uuid/qa-activity”Get a user’s Q&A activity summary.
Response
Section titled “Response”{ "success": true, "questions": [ { "id": "q-...", "question_id": 42, "question": "How do I configure 2FA?", "group_name": "IrregularChat: Security", "solved": true, "answer_count": 2, "created_at": "2026-01-25T10:30:00Z" } ], "answers": [ { "answer_id": 5, "question_id": 38, "answer": "You can find that in settings...", "group_name": "IrregularChat: Help", "is_solution": true, "upvotes": 3, "created_at": "2026-01-24T14:00:00Z" } ], "stats": { "questions_asked": 5, "answers_given": 12, "solutions": 3, "total_upvotes": 25 }}Wiki Search API
Section titled “Wiki Search API”GET /wiki/search
Section titled “GET /wiki/search”Search wiki articles using semantic search (OpenAI embeddings).
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | required | Search query (min 3 characters) |
limit | number | 5 | Results limit (max: 10) |
Response
Section titled “Response”{ "success": true, "query": "two factor authentication", "results": [ { "title": "Security Best Practices", "url": "https://wiki.irregularchat.com/security", "category": "Security", "tags": ["2fa", "authentication"], "similarity": 0.89, "excerpt": "Two-factor authentication (2FA) adds an extra layer..." } ]}Error Responses
Section titled “Error Responses”All endpoints return consistent error format:
{ "success": false, "error": "Error message description"}HTTP Status Codes
Section titled “HTTP Status Codes”| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request (missing/invalid parameters) |
| 401 | Unauthorized (invalid API key) |
| 403 | Forbidden (e.g., editing another user’s answer) |
| 404 | Not found (question/answer doesn’t exist) |
| 500 | Server error |
| 503 | Database not initialized |
Q&A Web Form
Section titled “Q&A Web Form”The web interface at qa.irregulars.io provides:
Public Pages (No Login Required)
Section titled “Public Pages (No Login Required)”- / - Homepage with recent questions
- /q/:id - View question with answers
- /leaderboard - Community rankings
Authenticated Pages (Login Required)
Section titled “Authenticated Pages (Login Required)”- /account - User’s questions and answers
- Answer submission forms
- Voting on answers
- Adding clarifications
Authentication Flow
Section titled “Authentication Flow”- Click “Login” button
- Redirect to Authentik SSO (
sso.irregularchat.com) - Authenticate with IrregularChat account
- Redirect back to Q&A with session
- Session stored in Cloudflare KV (24-hour expiry)
OIDC Configuration
Section titled “OIDC Configuration”The web form requires these OIDC endpoints:
| Setting | Description |
|---|---|
OIDC_ISSUER | Authentik issuer URL |
OIDC_AUTHORIZATION_URL | OAuth authorize endpoint |
OIDC_TOKEN_URL | OAuth token endpoint |
OIDC_USERINFO_URL | User info endpoint |
OIDC_LOGOUT_URL | Logout endpoint |
OIDC_CLIENT_ID | OAuth client ID |
OIDC_CLIENT_SECRET | OAuth client secret (set as Cloudflare secret) |
Integration with Signal Bot
Section titled “Integration with Signal Bot”How Questions Flow
Section titled “How Questions Flow”- Signal → User sends
!ask How do I...? - Bot → Saves question to PostgreSQL
- Web → Question appears at
qa.irregulars.io - Web → User submits answer via form
- Bot → Sends DM notification to original asker
- Signal → Asker can
!acceptor!rejectthe answer
Command Equivalents
Section titled “Command Equivalents”| Web Action | Signal Command |
|---|---|
| View question | !question ABC123 |
| Submit answer | !answer ABC123 <text> |
| Upvote | No equivalent (web only) |
| Add clarification | !clarify ABC123 <text> |
| Mark solved | !accept ABC123 A1 |
Examples
Section titled “Examples”List Open Questions
Section titled “List Open Questions”curl "https://signal.irregulars.io/questions?status=open&limit=10"Search Questions
Section titled “Search Questions”curl "https://signal.irregulars.io/questions?query=vpn&status=all"Submit Answer (Authenticated)
Section titled “Submit Answer (Authenticated)”curl -X POST "https://signal.irregulars.io/questions/42/answers" \ -H "Content-Type: application/json" \ -H "X-Api-Key: YOUR_API_KEY" \ -d '{ "answer": "Here is how to configure VPN...", "answererName": "Bob", "answererUuid": "uuid-...", "groupId": "abc123..." }'Get Leaderboard
Section titled “Get Leaderboard”curl "https://signal.irregulars.io/leaderboard?period=month&limit=10"Changelog
Section titled “Changelog”2026-01-26
Section titled “2026-01-26”- Added view tracking endpoint (
POST /questions/:id/view) - Added confidence level to answers
- Added sources array for citations
- Wiki semantic search integration