Org in a Box
API Reference

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"
}

On this page