Overview
Integrations — Overview
How any external app (wieeo, Zapier/Make, a customer's own script) integrates with Corteksa. There are exactly two directions, and everything below hangs off them:
- PULL — the app calls Corteksa's REST API to read and write records.
- PUSH — Corteksa sends the app HMAC-signed webhooks when records change.
Together they are a full two-way sync. An app authenticates once, then does both.
The whole picture
PULL — the app calls us
Every request carries a credential and flows through one gate, four layers:
- Credential — an API key (
crtk_live_…) or an OAuth access token (crtk_oauth_…). ApiAuthGuard— the single gate for humans and apps. It classifies the token, authenticates it, and attaches an acting-as-adminAdminUsercarrying the credential's scopes (super-admin bypass forced off).PermissionGuard+@RouteName— the scope check. A key only reaches a route whose scope it holds (read.{objectSlug},create.{objectSlug}, …).- RLS + workspace pinning — even past the gate, every query is scoped to the credential's workspace by Postgres row-level security (on the shared hyper DB).
What an app can pull/push through this gate — the complete external surface today:
| Capability | Endpoints | Scope |
|---|---|---|
| Identity | GET /me | any valid credential |
| Discovery | GET /object/active, GET /object/field/active/:slug | valid key (fields need read.{obj}) |
| Records — read | GET /object/data/:slug (+ search / lookup / facets / relations / logs) | read.{obj} |
| Records — write | POST/PUT/DELETE /object/data/:slug (+ bulk) | create/update/delete.{obj} |
| Webhooks | POST/GET/DELETE /webhooks/subscriptions, GET /webhooks/events | valid key (subscribe needs read.{obj}) |
Admin, billing, roles, workspace settings, and the key/app management endpoints are human-only — a credential can never reach them.
⚠️ Write bodies are keyed by field slug (e.g.
name-8fk2), not the display name. A display-name key can400on a required field. Discovery returns the slug.
Full endpoint list: Integration API Reference.
PUSH — we call the app
Instead of polling, the app subscribes an HTTPS endpoint and Corteksa pushes a
signed POST on every record change:
- Reuses the internal webhook engine (Bull delivery, retry/backoff, RLS-safe) — a "public subscription" is just a webhook owned by an API key.
- Each delivery is HMAC-SHA256 signed (
X-Corteksa-Signature: t=…,v1=…) with a per-subscription secret shown once at subscribe time. - No loops: an event caused by a write the app's own key made is never delivered back to that app — so a two-way sync can't echo.
Full contract + signature-verification recipe: Webhooks.
How an app connects — two supported paths
Both paths end at the same endpoints and the same scope model; they differ only in how the credential is obtained.
| Paste-key (simple) | OAuth "Connect" (published app) | |
|---|---|---|
| Credential | crtk_live_ API key | crtk_app_ client + crtk_oauth_ token |
| Who creates it | the customer (Settings → API Keys) | you register the app once (super-admin) |
| Connect UX | paste the key → validate via /me | "Authorize" redirect → code → token |
| Best for | self-serve, one workspace | a published multi-customer integration |
| Details | API Keys | API Keys → Phase 2 |
Why it scales
- One gate, any credential — adding a credential type is one change in the classifier; humans and apps share the same door.
- Scope = route name — a new endpoint inherits authorization for free.
- Global auth capability —
ApiAuthGuard's dependencies are app-wide, so opening a new controller to apps is a one-line guard swap, no wiring. - Webhooks extend, not fork — the push channel rode onto the existing engine; HMAC + loop-prevention layered on top.
- Guardrail tests encode the invariants — RLS pinning and "no write route may skip its scope check" fail CI automatically, so the surface stays safe as it grows.
Onboarding the next app costs nothing (paste-key) or one registration (OAuth). Exposing the next capability is incremental — a scope + a guard swap for pull, a catalog entry + a listener for push.