Skip to main content

Milestone 14: Integrations (webhooks and public API)

Purpose: Tenants with entitlement can configure webhooks and use the public REST API for tickets, messages, inboxes, tags (read and limited write; no outbound reply via API).

Exit state: WebhookEndpoint CRUD; event fan-out with retries and delivery log; Public API REST with API key auth and rate limit; OpenAPI spec.

Spec reference: §2.8 Integrations, §4.8 Webhooks, §4.9 Rate limits, §5.16 WebhookEndpoint, §5.17 WebhookDelivery, §5.18 ApiKey, §6.10 settings/webhooks, settings/api.

Prerequisites: M13. Entitlements (webhooks = Business+, public_api = Business+) from M10.


14.1 Webhooks

Tasks

  1. WebhookEndpoint CRUD

    • tRPC: list, create, update, delete. Table: tenant_id, url, secret (store encrypted or hashed), events (array of event types), enabled. Section 5.16. On create: generate secret; show once in UI ("Copy secret — you won't see it again"). Settings → Webhooks: list endpoints (URL, enabled, events, last delivery status); Add endpoint (URL, events multi-select); Edit/Delete. Gate by webhooks entitlement. Section 6.10.
  2. Event fan-out

    • When events occur (ticket.created, ticket.updated, ticket.assigned, ticket.status_changed, ticket.closed, message.received, message.sent, inbox.error): for each webhook_endpoint with that event in events[] and enabled=true, enqueue Inngest job with payload (event, event_id, tenant_id, created_at, data per Section 2.8). Payload and envelope per spec; HMAC X-InboxOps-Signature (SHA-256 of body with secret). Section 2.8.
  3. Delivery job

    • Inngest function: POST to endpoint URL with JSON body and signature header. On response: store webhook_deliveries row (endpoint_id, event, event_id, payload, response_status, response_body snippet, attempt, succeeded, delivered_at). Retry with exponential backoff; max 10 attempts; schedule per Section 2.8 (1m, 2m, 4m, ...). On success 2xx mark succeeded; on failure retry; after 10 failures mark failed and stop. Section 4.9.
  4. Delivery log UI

    • Settings → Webhooks: per endpoint, show last deliveries (table or expandable); status, response code, time. Optional: "Test" button to send test payload. Section 6.10. Retention: last 100 or 30 days; prune in cron (M16 or here).

Acceptance criteria

CriterionStatus
Admin can add webhook endpoint with URL and events; secret shown once; endpoint receives POST on events.Pending
Payload and signature match Section 2.8; signature verifiable with secret.Pending
Retries and delivery log work; failed deliveries visible in UI.Pending
Webhooks gated by Business plan (or webhooks entitlement).Pending
Webhook delivery log updates in real time when new deliveries complete (SSE or refetch so latest status visible without manual refresh).Pending

14.2 Public API

Tasks

  1. API key CRUD

    • tRPC: list (name, key_prefix, last_used_at, created_at); create (name → generate key, store key_hash and key_prefix, return full key once); revoke (set revoked_at). Section 5.18. Key format: ixk_ + random segment; prefix first 8 chars in UI. Settings → API: list keys, Create (name, show key once), Revoke. Gate by public_api entitlement. Section 6.10.
  2. REST API routes

    • Base path /api/v1. Middleware: extract Authorization Bearer token from header; hash and lookup api_keys; validate not revoked; load tenant_id; check tenant_entitlements for public_api; rate limit (e.g. 100 req/min per tenant, DB sliding window). Section 4.4, 2.8. Attach tenantId to request.
  3. Endpoints (Section 2.8)

    • GET /tickets (query: status, inbox_id, assignee_id, priority, page, per_page max 100). GET /tickets/:id (id = UUID or ticket_number). POST /tickets (body: subject, inbox_id, priority?, assignee_id?). PATCH /tickets/:id (subject, status, priority, assignee_id, inbox_id). GET /tickets/:id/messages (exclude internal notes). POST /tickets/:id/messages (body: body_html or body_text, type: internal_note — no outbound reply). GET /tickets/:id/tags, POST /tickets/:id/tags (tag_id or tag_name), DELETE /tickets/:id/tags/:tag_id. GET /inboxes, GET /inboxes/:id. GET /tags. All tenant-scoped; JSON responses; errors { error: { code, message } }. Pagination meta: page, per_page, total. Section 2.8.
  4. Rate limit

    • 100 req/min per tenant; 429 with Retry-After. Section 2.8. Implement in middleware or per-route (DB or in-memory counter with TTL).
  5. OpenAPI and docs

    • Generate or maintain OpenAPI spec; serve at /api/v1/openapi.json; optional docs UI at /api/v1/docs. Section 2.8.

Acceptance criteria

CriterionStatus
API key created and stored hashed; full key shown once; prefix in list; revoke invalidates key.Pending
All documented endpoints work with Bearer key; tenant scoped; 401/403/429 correct.Pending
No outbound reply via API (POST message with type internal_note only); document in spec.Pending
OpenAPI spec and docs available; public_api entitlement required.Pending
Ready for M15 (Marketing site).Pending

Milestone 14 sign-off

CriterionStatus
All tasks in 14.1–14.2 complete.Pending
All acceptance criteria met.Pending
Webhooks and Public API functional and gated by plan.Pending
E2E: Webhook CRUD and API key UI (see INDEX — Testing strategy).Pending
Ready for M15 (Marketing site).Pending