Every AI-CRM in the market can propose the same action. Actionary is the one where firing it twice produces the same outcome as firing it once. The reasoning provider is per-tenant — Anthropic, OpenAI, Gemini, or Bedrock. The landing is durable, retryable, auditable, because the schema, the indexes, and the code refuse to double-execute.
Actionary is designed with the metadata substrate first, then the human interfaces on top of it, then the agent on the same tool surface every human touches.
This page is the map. Follow any link to go deeper.
The substrate — schema is the contract
Every entity — built-in or defined by a platform admin through the UI — is a row in entities plus a list of rows in entity_fields. That one payload drives the SPA renderer, the admin REST API, the MCP tool surface, the search index, the audit log, and the CI gates. One source of truth. The agent reads the same schema every human interface reads.
New entities land through an in-app migration runner. SQL files under db/migrations/postgres/, applied under a database advisory lock, sha256-tracked for drift, split by phase — additive, expand, switch, contract, data — so the release pipeline runs them in the right window. No proprietary DSL. No sandbox-to-prod ceremony. No consultant on the critical path. More on the substrate.
The agent — MCP is the tool surface
The agent runs in-process inside core_api and drives the same MCP tool surface external clients see. Provider-agnostic — Anthropic, OpenAI, Bedrock, Azure OpenAI, Gemini. Per-tenant primary and fallback provider, with automatic failover on provider_unavailable or rate_limit, re-emitted as a visible badge in the composer.
Every multi-tool turn writes a reflection — a 1024-dimension vector embedding of what the agent did and how it turned out — into a per-tenant agent_reflections table. The next turn retrieves the closest matches by hybrid score and injects them as past_experience. Operators inspect, edit, promote, and evaluate the memory at /settings/agent/memory. Schema-drift triggers invalidate reflections whose referenced field is renamed or dropped, so memory tracks the schema.
Retrieval-Augmented Generation ships on the same primitives. Every upload — PDF, Word, PowerPoint, CSV, JSON, plain text, Markdown, and every audio and video container the meeting recorder handles — extracts, chunks, embeds, and lands in content_asset_chunks under the same row-visibility gate every SPA list applies. The search_upload_contents MCP tool returns real page numbers and time offsets. Citations read “page 14 of the Q4 board pack” or “at 08:42 in the Acme discovery call”. More on the agent.
Signals — external events become structured meaning
Install a Microsoft 365 connector once at /settings/mcp-clients. Schedule the inbox-signals workflow. Unread mail trawls every N minutes, an LLM step classifies decisions, risks, commitments, intent — and the platform lands the right CRM rows. The signals entity enforces a uniqueness constraint on (tenant_id, signal_source_id, source_external_id) so a re-issued webhook or a browser refresh converges on one row. More on Signals.
Workflows — durable execution on Postgres
The workflow engine is a DAG runtime that runs on Postgres, safe under multiple replicas without a separate durable-execution service. Step claims use durable queueing so a replayed step never double-inserts. Every step carries an idempotency key of the form sha256(tenant|event_id|run_id|step_id|action_type). Outbound webhooks are HMAC-SHA256 signed, retriable, dead-lettered. A downstream receiver that respects the key converges to one delivery.
Connectors — a typed adapter catalogue
app_connector_providers is the platform-curated catalogue. tenant_app_connectors is the per-tenant install. Per-user OAuth tokens are encrypted at rest with a subsystem key derived from PLATFORM_MASTER_KEY via HKDF-SHA256. Four auth shapes cover every provider: OAuth with dynamic client registration, static OAuth credentials, static bearer token, and platform-brokered OAuth. Microsoft 365 is the first shipped instance; the pattern is generic.
Surfaces — four hosts, one data plane
Staff console at console.{base}, tenant admin at app.{base}, public portal at portal.{base}, embeddable widget at embed.{base}. Four separately built bundles on four origins with four cookies over one shared API. A compromise in the public form renderer physically cannot reach an authenticated admin session — the code does not exist at that origin. Marketing lives on the apex and www.{base} and never touches product data.
Trust — as an architectural property
One PLATFORM_MASTER_KEY derives one 32-byte key per subsystem via HKDF-SHA256. Postgres row-level security applies to every tenant_id table, keyed on a request-scoped tenant variable, enforced by a database role that cannot bypass the policy — a query that forgets the tenant clause returns zero rows. Platform-admin access is signalled only by session_kind === "staff_platform"; the boolean is_root is banned from every table, payload, and JWT claim. Idle timeout on staff sessions is three times shorter than tenant. Every list response carries per-row _permissions:{can_update, can_delete} and the UI hides actions the caller cannot complete. Two AST-based CI gates fail the build if any layer regresses. More on trust.
Platform — open standards at every boundary
PostgreSQL 16, Redis 7, S3-compatible object storage, OpenTelemetry, MCP, OAuth 2.0 + DCR + PKCE, Docker, Caddy. pg_dump produces a portable backup of the whole stack, retrieval index included. Storage adapter swaps to Cloudflare R2, MinIO, or Backblaze B2 with one env var. LLM provider is per-tenant configuration with bring-your-own key. No proprietary DSL. No vendor SDK on the critical path.
The takeaway
The human work of curating relationships and the agent work of acting on them become the same work, on the same data, through different interfaces. Every write path is idempotent by construction. Every surface reads the same schema. Every trust boundary is load-bearing in infrastructure, held in the platform layer rather than a router constant. That is the whole product on one page — and every claim on it is a pattern in the code you can grep for. Start with the substrate, or read the pricing.