Gatetrol API - v1

REST API for inbound classification, autoreply, voice receptionist, and outbound webhook delivery. All endpoints respond with application/json unless noted.

Authentication

Every protected endpoint accepts an API key issued from the dashboard at /app/api_keys.php.

Authorization: Bearer gtl_live_<your_key>

Alternative headers: X-Gatetrol-Key: gtl_live_…, or query param ?api_key=… (discouraged - leaks via logs).

Browser dashboard auth: session cookie set by the magic-link flow.

Endpoints

GET /v1/health.php

Liveness + DB ping. Public, no auth.

GET /v1/settings.php

Return the per-tenant settings (timezone, business hours, urgency keywords, VIP list, autoreply templates, digest config, voice greeting). Defaults are filled in for null fields.

PUT /v1/settings.php

Update one or more fields. Send JSON body with whitelisted keys: timezone, business_hours_json, urgency_keywords, vip_emails, personal_contacts, autoreply_customer, autoreply_unknown, autoreply_after_hours, forward_personal_to, digest_enabled, digest_local_time, alert_channel, alert_email, paused_until, voice_business_name, voice_greeting_text.

GET /v1/classifications/list.php

List classified messages. Query params:

Returns { ok, data: [...], next_cursor, count }.

POST /v1/classifications/resolve.php

Mark a classification resolved. Body: { "id": 123, "by": "optional label" }.

POST /v1/classifications/draft_reply.php

Get an AI-drafted reply using the tenant's KB context. Body: { "id": 123, "instructions": "optional extra guidance" }. Returns { ok, draft }.

POST /v1/classifications/sms_reply.php

Send an SMS reply to the sender of an SMS classification, via the tenant's configured carrier. Body: { "id": 123, "body": "Your reply" }. Marks the original resolved.

Canned responses - GET/POST /v1/canned/{list,save,delete}.php

Save body: { id (0=new), label, body, classification_filter, channel_filter, sort_order, active }.

Knowledge base - GET/POST /v1/kb/{list,save,delete}.php

Save body: { id (0=new), label, body, tags, active }.

API keys - GET/POST /v1/keys/{list,create,revoke}.php

Session-only (cannot use API key to create another API key). New key is shown once at creation.

Webhooks - GET/POST /v1/webhooks/{list,create,delete}.php

Register a URL to receive classification events. Signing secret is shown once on creation. Verify the X-Gatetrol-Signature header against hash_hmac('sha256', $rawBody, $secret).

Billing - POST /v1/billing/{checkout,portal}.php

Checkout body: { plan: "starter|email_sms|pro|concierge", success_url, cancel_url } → returns Stripe Checkout URL.

Portal body: { return_url } → returns Stripe Customer Portal URL. Customer must have completed Checkout first.

Tenants with billing_mode='external_jobtrol' cannot use these endpoints (their billing is handled by Jobtrol).

Inbound webhooks (configure with your carrier)

Outbound webhook payload format

Gatetrol POSTs JSON to your registered URL(s). Verify X-Gatetrol-Signature (HMAC-SHA256 of the raw body using your signing secret). Headers:

Body shape:

{
  "event": "classification.urgent",
  "tenant_id": 5,
  "timestamp": "2026-05-14T17:42:00+00:00",
  "data": {
    "classification_id": 123,
    "tenant": { "id": 5, "email": "..." },
    "message": {
      "channel": "email",
      "sender_address": "...",
      "sender_name": "...",
      "subject": "...",
      "body_preview": "..."
    },
    "classification": {
      "class": "urgent",
      "confidence": 0.92,
      "reason": "..."
    }
  }
}

Respond with HTTP 2xx within 8 seconds; Gatetrol retries failed deliveries on a 60s → 5m → 30m → 2h → 12h → 24h schedule, then marks dead.

Magic-link sign-in (dashboard)

  1. POST email to https://gatetrol.com/v1/auth/request.php → mails a sign-in link (30-min TTL).
  2. Clicking the link hits /v1/auth/verify.php?token=..., which establishes a session and redirects.
  3. Sign out: /v1/auth/logout.php.

Error format

{ "ok": false, "error": "code_snake_case", "message": "Human-readable detail (optional)" }