Skip to content

Key concepts

A single reference for the named pieces of Turbobase, the ERP operations and development platform for Oracle NetSuite. Read this once and the rest of the docs read straight through.

New here? Start with Getting started for the path to a live instance, then come back to this page for how the pieces fit together. Every word it uses is defined once, on the Glossary.

If you only have ten minutes:

  • A source connector plugs a system of record (NetSuite, Stripe, and others) into your instance via a small, fixed contract.
  • The sink is your Postgres — every source flows into one canonical schema you own.
  • A sync event is one row’s trip in either direction; a sync failure is an event that didn’t land.
  • Hooks shape outbound writes (validate, rewrite); guardrails gate privileged operations (allow, audit, confirm, deny).
  • Atlas consolidates the same entity across multiple source systems.
  • Tasks are scheduled jobs — chat prompts, saved views, system maintenance — your instance runs on a cron.
  • Channels deliver human-in-the-loop notifications (Slack, …) for confirmations and alerts.
  • The audit log is one append-only table that records every privileged op and decision.

The rest of the page expands each.


A pluggable link between Turbobase and one external system. The contract is small enough to fit in a paragraph; the engine, queue, conflict resolver, named agents, hooks, guardrails, and chat UI never branch on which connector is attached.

A connector implements five methods, give or take:

Method Purpose
verifyWebhookSignature(req) Confirm an inbound webhook came from the source (HMAC, OAuth, etc.).
parseEnvelope(body) Normalise the source’s webhook payload into a canonical envelope.
writeBack(event, patch) Push a canonical patch back to the source system.
listChanges(type, cursor?) Iterate records modified since a cursor (delta sync).
schemaIntrospect(account) Snapshot the source’s schema — custom fields, available record types.

Stateful connectors (NetSuite) add lifecycle hooks for governance, auth routing, and session-expired UX. Stateless connectors (Stripe) skip most of those — they’re webhook-led with a single-account API key. Both shapes ship today and serve as the reference for new connectors.

Connectors that are not yet live ship through a managed onboarding flow rather than a public extension surface. Email support@turbobase.io if you need a source we don’t list.


Two paths run in parallel for every connector:

  • Source → your Postgres (inbound). The source emits a webhook (or the engine pulls deltas); the envelope is verified and queued; the canonicalizer projects the source-shaped record onto your canonical schema.
  • Your Postgres → source (outbound write-back). An app write marked originating_system = 'hybrid' triggers the realtime listener; hooks fire; the connector pushes to the source.

A standalone instance (source_system='standalone') is the degenerate case: Turbobase is the system of record, webhook ingress and write-back both no-op. The schema, RLS, hooks, guardrails, audit log, channels, and chat UI are unchanged.


The Postgres database every source writes into and every read serves from. “Sink” is the data-flow term; the rest of the docs call the same thing your canonical mirror — one concept, two vantage points. There’s exactly one per instance, and you own it.

  • Schema namespace: turbobase (never public). More than two hundred tables today, spanning customers, sales orders, invoicing, fulfillment, payments, items, vendor bills, purchase orders, journal entries, accounting periods, chart of accounts, projects, and the supporting lookups and line tables.
  • Every record carries source_system, external_id, last_modified_date (when the source last changed it), last_modified_pg, originating_system, synced_at and sync_version — enough metadata to reconstruct provenance and resolve conflicts. A record you can create in Turbobase also carries a pg_row_id of its own, so it exists before the source has given it an id.
  • Reads exit through PostgREST and Realtime; writes that need a round-trip to the source go through the write-back queue.
  • Row-level security scopes every PostgREST call to the calling user (has_capability, scopes_match). The anon key never bypasses RLS.

Sync engine: events, failures, conflict resolution

Section titled “Sync engine: events, failures, conflict resolution”

The generic core that drives both directions. It doesn’t know what NetSuite or Stripe is — it knows about connectors, canonical patches, delta cursors, and queues.

  • sync_event — one row per record-trip. direction is source_to_pg or pg_to_source. State machine: pending → in_flight → committed | failed, with two sidings — awaiting_confirmation while a guardrail holds the write for someone to decide, and quarantined.
  • sync_failure — emitted when an event fails. Carries category, classification, the offending payload, and the connector’s error. The Mechanic agent subscribes to this table over Realtime.
  • Delta cursor — a per-record-type lastModifiedDate checkpoint. The cursor only advances on a clean run; a partial run safely re-scans the same window.
  • Conflict resolver — last_modified_pg against the last sync touch (synced_at) on one side, and the timestamp the source itself reports on the inbound record on the other, decide whether both sides changed in the same window. Latest write wins; on a tie the source does. Per-field ownership rules resolve which side wins on a per-column basis.

Hooks intercept write-backs before they leave for the source. Two phases:

  • validate returns validation errors. A non-empty list short-circuits — the event becomes a sync_failure with category='hook_validation'.
  • rewrite returns a new canonical patch. Composes left-to-right; the next hook sees the previous one’s output.

Hook kinds are code; hook rows are admin-defined and instance-wide, selected by record type. RLS gates writes to admins only; a default guardrail also denies non-admin chat writes to the hook table.

Shipped kinds include require-field (fail when a top-level canonical field is missing), source-specific arithmetic helpers (for example, NetSuite line amount = quantity × rate when missing), and custom-validator / custom-rewrite — admin-supplied JavaScript run in a sandboxed isolate with strict heap and wall-clock limits.

Hooks also have provenance — code-baseline (Code baseline, shipped with Turbobase), operator (CLI deploy, what you deployed yourself), mechanic (Mechanic, learned from a real sync rejection) or learning (Learning, raised proactively) — which feeds the learning loop (see Learning mode below). It is the Origin column on the extensions page, and you can filter on it.


A policy engine that gates privileged operations — anything that mutates source-side configuration (custom fields, role permissions, deployment surface) or pushes high-value writes (large dollar amounts, AI-driven mutations on sensitive fields).

Where hooks shape the content of a write, guardrails decide whether the operation itself runs at all. Every privileged op declares a descriptor:

{ source: 'netsuite', op: 'customfield.delete',
risk: 'destructive',
target: 'customrecord_my_table.custrecord_signing_key',
actor: 'agent:mechanic' }

The engine resolves one of four decisions:

Decision Effect
allow Execute, no audit row.
allow_with_audit Execute, write an audit row (the default for most ops).
require_confirm Block, write a pending-confirmation row, fan out via channels, wait for an operator.
deny Refuse, write an audit row with status='denied'.

Policy is layered per source × per op-pattern × per risk × per actor. Defaults ship out of the box; admins layer instance-specific rules on top from the app. First match wins; the catch-all is the per-risk default.

The headline use case is “AI can’t update salesorder.amount, humans can” — same op, different actor, different decision.


One append-only table — turbobase.audit_log — captures every privileged op, every guardrail decision, every authentication event, and every user-management mutation. Written through a SECURITY DEFINER function so callers don’t need direct insert grants.

Row shape (abbreviated):

column what it carries
actor wizard:setup / cli:<name> / agent:<name> / user:<auth.uid>
action Verb. Connector ops use connector.<source>.<op>; guardrail decisions use guardrail.*; auth events use auth.*.
target_table, target_id What was acted on.
before, after JSONB snapshots — deletes capture before, creates capture after, updates capture both.
correlation_id Confirmation token (when require_confirm fired) or caller-supplied id linking related ops.
context { decision, reason, risk, args, result } — the full guardrail trace.

The /audit page in your Turbobase app gives operators a date-range form, quick-filter chips, and per-actor / per-target drill-down.


A cron-like scheduler built into your instance. A worker polls turbobase.scheduled_task once a minute, picks rows whose next_run_at has elapsed, runs each task as the owner (per-run JWT — RLS, scopes, and capabilities all apply), records a scheduled_task_run plus a notification, and recomputes next_run_at from the task’s schedule.

Three task body kinds:

body.kind What it does
chat_prompt Invoke Pilot, your AI assistant, with a stored prompt — for example, “Email me yesterday’s at-risk sales orders”.
saved_view Re-run a pinned saved view and deliver the result.
system_job Built-in maintenance job (delta resync, schema-drift detection, playbook rebuild, learning rebuild). No per-user JWT — runs system-owned.

Delivery is either in-app (notification row) or out-of-band via a channel (Slack — see Channels below). Five consecutive failures auto-pause the task and notify the owner. The recurring sync jobs are exempt from that five-failure rule — the delta sync keeps its schedule through failure streaks (failures stay visible in the run history and on the sync-health report). The built-in jobs that use AI are exempt too when the only thing missing is an AI key: each keeps its schedule, does the part of its work that needs no AI, records in its run history that it produced no AI output, and starts using AI the moment you add a key. When a recurring platform job is failing because a source refused our sign-in — the scheduled sync itself included — the sync-health report says so on that job’s own row rather than counting it as one more failing job, and tells you to reconnect the source from Data → Sources, since a fresh sign-in is the only thing that mints a new credential. Those rows clear themselves on the first run that succeeds; there is nothing to dismiss. Nothing stays broken unnoticed, though: any system job still failing 24 hours after its streak began raises an escalation your operator is alerted on. The two document-memory sweeps (uploaded-document text extraction and embedding) additionally stop at that point rather than keep calling a dependency that is refusing them; the reason is recorded on the row, and they stay stopped until support resumes them, because platform jobs are not writable from the tenant surface. The sync jobs are deliberately not stopped — a source outage that ends should not leave your data sync switched off.

Constraints plus RLS enforce the (system=true ↔ owner=null) split: any authenticated caller can read system rows, only admins or the service role can mutate them.


The multi-source identity-resolution layer. When two sources both emit a customer record for the same real-world organisation, Atlas decides whether to link them, create a new canonical entity, or surface the pair for human review.

Pipeline (deterministic tier — what ships today):

  1. Read identity fields off the canonical payload via the paths declared in the record type’s identity-field map. Seven dimensions can match: email, phone, taxId, domain, gtin (a product’s barcode), externalKey and, for a person, name-plus-employer.
  2. Normalise each — lowercase email, E.164 phone, digits-only tax id, and a web address reduced to a bare host. A host that identifies nobody (a free mail provider, say) is dropped rather than matched on, so two unrelated customers who both use one never consolidate.
  3. Query for exact matches on each populated key.
  4. Single match → link. Multiple matches → review queue with both candidates. No match but at least one identity present → create a new canonical row. Zero identities → create new (no keys to dedup on; future embedding tier reconciles).

Embedding and LLM tiers are wired but reserved for when dedicated workers ship.

Important: Atlas consolidates the records that name a thing — customer, vendor, contact, employee, partner, and the items you sell (matched on barcode or SKU). Transaction-shaped records (invoice, payment, fulfillment) stay per-source — they’re ledger events, not identities. A future cross-source ledger bridges those transactions without collapsing them.


Pluggable fan-out for human-in-the-loop events:

  • confirmation_requested — a guardrail hit require_confirm; an operator needs to approve or deny within the configured window.
  • confirmation_decided — somebody approved or denied; the original requester and the other channels get the resolution.
  • Task failure notifications, scheduled-task delivery, schema-drift alerts.

Slack is the channel your instance exposes today: connect it from Settings → Slack and it registers at boot. Every require_confirm decision ends in a Slack message with the approval link pre-baked.


Turbobase ships with four named agents. Each one has a specific job, a specific permission scope, and a specific surface where you talk to it. Future releases extend the cast under the same naming convention — recognisable English job titles.

Agent Role
Conductor The behind-the-scenes coordinator. Routes events to the right specialist, applies your permission rules, owns notification delivery and skill loading. Long-lived; supervises the others.
Mechanic Watches your sync, fixes what it can, asks for help on what it can’t. Subscribes to sync_failure; resolves known patterns (record locked, insufficient permission, field validation, governance limit, missing dependency) autonomously today, and is designed to grow into bundle drift, role permissions, and schema drift.
Pilot Drives Turbobase in plain English. Answers questions over the canonical schema, builds apps from natural-language prompts, runs scheduled tasks. The same agent runs in the app and on Slack.
Concierge Walks the operator through onboarding. Provides click-paths for the source you’re connecting, validates credentials, kicks off the initial sync.

Why these four (and only these four) — the cast is small on purpose. Memory, learning, and skills are traits all four agents share, not separate agents. Hooks are user-written code, not agents. Guardrails are policy, not agents. New agents added later will get their own English job-title names (Auditor, Watchdog, Archivist, …) so the cast stays legible to a non-technical reader.

How the four agents handle multi-system sync

Section titled “How the four agents handle multi-system sync”

Every agent works against the canonical layer, not source-specific code. That’s what makes a single cast scale across multiple sources instead of multiplying with each new connector.

  • Conductor is source-agnostic by construction. It dispatches events whose source_system is just a column.
  • Mechanic operates on failure patterns expressed in connector-neutral terms. “Record locked” covers a NetSuite record-lock today and a Stripe idempotency conflict tomorrow. “Governance limit” covers NetSuite governance windows and Salesforce daily-API caps.
  • Pilot queries the canonical schema over PostgREST plus RLS. It doesn’t know which connector wrote a given row. A question like “show me this customer’s billing across systems” naturally joins NetSuite, Stripe, and Salesforce data once the consolidation lands — no new “multi-source mode” in Pilot.
  • Concierge ships per-source onboarding steps, but the runtime is the same across all of them. Each new connector contributes one step — its credential validator plus introspection call.

The same per-instance permission tiers and the same memory (“subsidiary 7 mandates a class on every line”; “this customer’s payment terms are NET 45”) apply across every source your instance has wired up. You don’t get a per-connector agent farm; you get one cast that learns about your business and applies that knowledge wherever your data lives.


Permission tiers (AI agent + per-instance)

Section titled “Permission tiers (AI agent + per-instance)”

How autonomously the Mechanic is allowed to act on a given failure pattern. Three tiers, configurable per instance per pattern:

Tier Behaviour
one_tap The agent stages the fix and sends a one-tap approval to the configured channels. This is the default for every failure kind — a fresh instance applies nothing on its own.
auto_apply The agent applies the fix directly. You promote a pattern here once you trust it; your instance also offers to promote one after 20 resolutions it got right with high confidence.
escalate The agent posts a structured recommendation but does not act.

Tier choices are recorded in the audit log; they layer cleanly with guardrails — a require_confirm rule always wins over an auto_apply tier.


  • Skills — agent capability bundles. Each bundle packages prompts, tools, and policies for a specific class of problem (for example, “record-locked retries”). Some ship with Turbobase; you can write and publish your own — see Agent skills.
  • Playbooks — longer operating manuals the agent consults when a skill isn’t pre-baked. Rebuilt nightly from the failure-resolution corpus.
  • Learning mode — a phase every instance starts in. While in learning, every recurring sync_failure cluster becomes a hook candidate; admins review on the extensions page and apply, edit, or dismiss. Two kinds of failure can land a hook on their own, above 0.85 confidence and as provenance='mechanic': a locked record and a governance limit. Everything else always comes to you — a field the source rejected, a missing permission, a missing dependency and a refused sign-in are never auto-applied, because each needs a decision only you can make. The top bar shows a soft “Learning” indicator, with a three-item Steady checklist (7 days active, fewer than 2 novel failures in the last week, hooks reviewed). The offer to promote to Steady gates on your write error rate rather than on the checklist, so clearing all three does not by itself move you on.

What one instance has learned moves to another: turbobase ext export pulls your hooks down into a project, and deploying that project into a second instance carries them over — so a sandbox’s learnings can promote to a fresh production instance.


The pipeline for changing your instance. A proposal is a drafted, evidence-cited change; you shape it conversationally with the AI in Studio, the chat-first authoring canvas, then mark it complete to freeze it. Freezing mints solutions — the concrete deliverables that realize the proposal. Solution kinds are hook, source_change (shown as Customization), conduit, config, native, documentation, app, data_manipulation and skill. Solutions ship through releases: every deploy is tracked, audited, monitored against a pre-release baseline, and reversible. Full surface: Development.


The customization-analysis surface for a connected source. It inventories what an account has been customized into — custom fields, records, lists, scripts, workflows, roles, forms, and saved searches — and sorts the risk it carries into five families: inventory, GL posting, fulfillment, data integrity, and security. Findings don’t stay a static report; they enter the development pipeline as signals and proposals. NetSuite-specific today. Full surface: Analyzer.


Internal apps built on your canonical data and served inside your instance at /a/<slug>, with an end-user launcher at /apps. An app is generated from a plain-English brief through the proposal pipeline — it is one of the solution kinds — and your instance keeps the app’s source of record so it can be refined later. Apps act as the signed-in user, so the same RLS applies to them as to everything else. Full surface: Build apps.


Your instance’s living memory of how the business actually runs — a tree of markdown pages organised process-first, with drafts, immutable published releases, and comments. It is both somewhere your team reads and the grounding Pilot answers “how do we do X” from. Full surface: Knowledge base.


A single column on every canonical row that records who wrote this most recently — source (the external system) or hybrid (Turbobase’s own write path). It’s the linchpin of the realtime listener: only originating_system='hybrid' writes enqueue a write-back, so source-driven inbound writes don’t bounce back as outbound writes and create a feedback loop.

Enqueuing is itself gated per source: write-back ships off, so until it’s enabled for a given source the hybrid write lands in your canonical mirror without being pushed upstream.


Every word on this page, and every word you meet elsewhere in Turbobase, is defined once — on the Glossary. That page is the single place the vocabulary lives, so a word cannot mean one thing here and something else there.


Questions the docs don’t answer? Email support@turbobase.io.