API Quickstart¶
Hands-on: from "fresh Railway deploy" to "first authenticated request" in under five minutes.
0. Prerequisites¶
- XTV-SupportBot deployed with
API_ENABLED=true(see Railway deployment) - The bot is a member of your Telegram admin forum supergroup and you
are in
ADMIN_IDS curl+jqinstalled locally
Throughout this page we use the public Railway URL as $BASE:
export BASE=https://your-bot.up.railway.app
1. Verify the API is live¶
curl -sS $BASE/health | jq .
Expected:
{"ok": true, "version": "0.9.0"}
If you get a hang or an HTML error page: the bot isn't serving HTTP
yet. Double-check API_ENABLED=true and Procfile is web: python
main.py.
2. Mint an API key¶
Open a DM with your bot and run:
/apikey create admin:full
The bot replies once with the plaintext key — copy it immediately, you will not see it again (it's SHA-256 hashed at rest). Save it into the env of whichever tool will call the API:
export XTV_KEY=xtv_abcdef1234567890abcdef1234567890abcdef12
Scopes matter
admin:full grants everything. For a production integration
create a narrow key per consumer: tickets:read, analytics:read,
webhooks:write, etc. See authentication for the
full scope list.
3. Your first request¶
curl -sS $BASE/api/v1/tickets?limit=5 \
-H "Authorization: Bearer $XTV_KEY" | jq .
Response:
{
"count": 5,
"items": [
{
"_id": "…",
"status": "open",
"priority": "high",
"created_at": "…",
…
}
]
}
4. Browse OpenAPI in a browser¶
Visit $BASE/api/v1/docs — Swagger UI loads with every route, every
schema, and a "Try it out" button. Paste your key into the green
Authorize button (format Bearer xtv_…) and every subsequent
request inherits it.
For a cleaner reading view: $BASE/api/v1/redoc.
5. Write endpoints (Phase 4.7)¶
Close a ticket:
curl -sS -X POST $BASE/api/v1/tickets/<id>/close \
-H "Authorization: Bearer $XTV_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "resolved"}' | jq .
Install a project from a built-in template:
curl -sS -X POST $BASE/api/v1/projects \
-H "Authorization: Bearer $XTV_KEY" \
-H "Content-Type: application/json" \
-d '{"template_slug": "billing", "project_slug": "pay", "name": "Payments"}' | jq .
Subscribe a webhook:
curl -sS -X POST $BASE/api/v1/webhooks \
-H "Authorization: Bearer $XTV_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/hook", "events": ["ticket.closed"]}' | jq .
The response includes a one-time secret — save it to verify future
deliveries (see webhooks).
6. JavaScript fetch example¶
const BASE = "https://your-bot.up.railway.app";
const KEY = localStorage.getItem("xtv_key");
async function recentTickets() {
const res = await fetch(`${BASE}/api/v1/tickets?limit=10`, {
headers: { Authorization: `Bearer ${KEY}` },
});
if (!res.ok) throw new Error(await res.text());
const body = await res.json();
return body.items;
}
The admin SPA in web/ uses this exact pattern.
7. Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
401 missing_bearer |
Forgot the Authorization header |
Add -H "Authorization: Bearer $XTV_KEY" |
401 invalid_key |
Wrong / revoked / typo key | Mint a new one with /apikey create |
403 insufficient_scope |
Key lacks a required scope | Revoke + recreate with the right scope, or grant admin:full if appropriate |
404 on /api/v1/… |
API not started | Set API_ENABLED=true, restart |
503 database_unavailable |
MongoDB down or unreachable | Check MONGO_URI and the Mongo add-on |
429 |
Rate-limit hit | Raise API_RATE_LIMIT_PER_MINUTE or throttle |