Gateways API
REST endpoints for managing gateway configs, paired users, and pairing codes.
Overview
Gateway endpoints manage the DB-backed pairing system for the two channels that use the
pairing flow: telegram and teams. Slack, Discord, and Email gateways are configured
through environment variables / Docker Compose profiles and do not participate in these
REST endpoints today (they pre-date the pairing UI and rely on per-channel bot install
flows instead).
All routes require authentication. When ORGINABOX_API_KEY is not configured (local dev),
auth is bypassed. In production, callers must have the admin.manage_providers RBAC
permission.
kind is validated against ["telegram", "teams"] in
packages/core/src/http/routes/gateways.ts — other values return 400.
Endpoints
List Gateways
GET /v1/gateways
Returns all configured gateways. Bot tokens and webhook secrets are never returned.
{
"gateways": [
{
"id": "uuid",
"kind": "telegram",
"botUsername": "orginabox_bot",
"botId": "1234567890",
"webhookUrl": "",
"enabled": true,
"createdAt": "2026-04-19T...",
"updatedAt": "2026-04-19T..."
}
]
}
Create Gateway
POST /v1/gateways
{
"kind": "telegram",
"botToken": "1234567890:ABC-...",
"botUsername": "orginabox_bot",
"botId": "1234567890"
}
The bot token is encrypted with AES-256-GCM before storage. Returns 201 with the created gateway (token omitted).
Update Gateway
PATCH /v1/gateways/:id
{
"enabled": false
}
Or rotate the token: { "botToken": "new-token" }.
Delete Gateway
DELETE /v1/gateways/:id
Cascades to all paired users and pairing codes.
Register Webhook
POST /v1/gateways/:id/webhook/register
{
"webhookUrl": "https://your-domain.com/telegram/webhook"
}
Calls api.telegram.org/setWebhook, generates a random HMAC secret, stores it encrypted. The URL must be HTTPS and not point to private network ranges.
Returns 502 if the Telegram API is unreachable.
Paired Users
List Paired Users
GET /v1/gateways/:id/paired-users
{
"pairedUsers": [
{
"id": "uuid",
"gatewayId": "uuid",
"externalUserId": "123456789",
"externalUserName": "johndoe",
"platformDisplayName": "",
"pairedAt": "2026-04-19T..."
}
]
}
Revoke Paired User
DELETE /v1/gateways/:id/paired-users/:externalUserId
The user will be asked to re-pair on their next message.
Pairing Codes
List Pending Codes
GET /v1/gateways/:id/pairing-codes
Returns only unexpired, unconsumed codes.
{
"codes": [
{
"id": "uuid",
"gatewayId": "uuid",
"code": "ABCD1234",
"externalUserId": "123456789",
"externalUserName": "johndoe",
"expiresAt": "2026-04-19T23:00:00Z",
"createdAt": "2026-04-19T22:00:00Z",
"consumedAt": null
}
]
}
Approve Code
POST /v1/gateways/:id/pairing-codes/:code/approve
Marks the code consumed and inserts a gateway_paired_users row atomically. Fires pg_notify('orginabox_gateway_events', ...) so connected SSE clients advance automatically.
Returns 404 if the code is not found or has expired.
{ "ok": true, "user": { "externalUserId": "123456789", "externalUserName": "johndoe" } }
Reject Code
DELETE /v1/gateways/:id/pairing-codes/:code
Deletes the pending code without creating a paired user.
SSE Events
GET /v1/gateways/:id/events
Server-Sent Events stream. The server polls the DB every 2 seconds and emits events when pending codes exist. On reconnect, clients should re-fetch via GET /v1/gateways/:id/pairing-codes to restore state.
Event: pairing.code_created
{
"kind": "pairing.code_created",
"codes": [{ "code": "ABCD1234", "externalUserId": "123456789", ... }]
}
Event: pairing.code_approved (fired by the approve endpoint)
{
"kind": "pairing.code_approved",
"gatewayId": "uuid",
"userId": "123456789"
}