An AI-CRM without safe-to-retry execution has one failure mode. The model reasons, produces a plausible action, and either fires it once and forgets — or fires it twice and creates two of everything. Every gap between the reasoning surface and the execution surface becomes a support ticket.

Actionary closes the gap in the substrate. Reasoning proposes; the platform lands. A tool call that fires twice — a network retry, a browser refresh, a re-issued webhook, an LLM stutter — converges on one record, one grant, one download, one signal, one webhook delivery. The guarantee lives in the schema, the indexes, and the code paths that touch them.

That is what makes the agent trustable at enterprise scale.

Propose-then-execute on the tool surface

The MCP propose_entity_update tool stages a diff in Redis with a 900-second TTL. A separate execute_entity_update call commits it. The model proposes; the operator or a downstream check confirms; the platform applies.

A naive tool-use loop fires the destructive action inside the same turn the model proposes it. If the API returns 502 mid-flight and the client retries, the tenant gets two records, two invoices, two emails to the customer. Actionary treats destructive actions as a two-phase commit by default.

The destructive-confirm gate lives on the agent_pending_tool_confirmations table with a per-(conversation_id, tool_use_id) unique constraint. The pending tool-call block stays out of the assistant message until confirmation — the message protocol stays self-consistent across the pending → confirmed → tool_result transition. Every tool-call block resolves. Protocol-level 400s hours later stay off the table.

Partial-unique-index guards on every live-row primitive

Every shared primitive uses the same pattern: a partial unique index gated on the tombstone column. record_access_unique_live on WHERE revoked_at IS NULL. Sibling paths cover auth_sessions, partner_sessions, external MCP OAuth clients, and share-token uniqueness on external recipients.

Granting the same person access to the same record twice creates one row. The transfer-content flow that moves ownership when a user is offboarded is atomic on this primitive: grant the target, revoke the departing user’s live grants, in one transaction, idempotent under retry.

Durable workflow execution on Postgres

The workflow engine is durable execution today. FOR UPDATE SKIP LOCKED step claims run safely across multiple replicas from a single Postgres queue. workflow_run_steps uses ON CONFLICT (workflow_run_id, workflow_node_id) DO NOTHING — a replayed step never double-inserts. Dead-letter table workflow_dead_letters captures every terminal failure with retry backoff.

Every workflow step carries an idempotency key: X-Workflow-Idempotency-Key = sha256(tenant|event_id|run_id|step_id|action_type). Outbound webhooks from the engine present the same key on every retry — a downstream receiver that respects it converges to one delivery.

The token-passing graph shape follows industry-standard durable-orchestration semantics. Postgres is the durable substrate — one system to keep alive in the deploy topology.

Public writes deduplicate at the boundary

Idempotency-Key header honoured on POST /public/forms/{form_id}/submit with a one-hour trailing lookback window. UNIQUE (tenant_id, signal_source_id, source_external_id) WHERE both NOT NULL on the Signals entity — a form submission → signal round-trip converges even if the fan-out retries mid-flight.

The same shape covers the Stripe billing bridge — a Stripe usage-record retry never double-charges a tenant. Every connector that lands data through Actionary inherits the same boundary discipline.

Outbound webhooks — signed, retried, dead-lettered

Public-surface webhook fan-out signs every delivery with HMAC-SHA256 on the X-Actionary-Signature header. The delivery ledger carries a partial index on (next_retry_at IS NOT NULL AND succeeded_at IS NULL) so a scheduler walks the retry queue efficiently.

Every retry carries the same signature. Every terminal failure lands in a dead-letter table an operator can see at /platform/workflow-dead-letters. A downstream receiver that respects the signature and the delivery id converges to one processed event — regardless of how many times the network forces a re-send.

Asset-download tokens redeem the same way. Issue once; redeem many; the same asset comes back, never a fresh mint. A compliance auditor points at one row per download event, no matter how many refreshes the user hit.

350+ ON CONFLICT clauses across the schema — chosen deliberately

The split across the service code and the migrations is 290 DO NOTHING to 87 DO UPDATE. Every write path is either idempotent by construction or explicitly upsert — every path is deterministic under retry. Three load-bearing examples:

  • workflow_event_logON CONFLICT (tenant_id, event_id) DO NOTHING. The same external event never triggers two runs.
  • workflow_run_steps — safe step re-creation across retries.
  • agent_pending_tool_confirmationsON CONFLICT (conversation_id, tool_use_id) DO UPDATE. Re-stage a pending confirmation without duplicating.

Every schema migration applies through an in-app runner with sha256 drift detection. A schema_migrations tracker records every apply with content hash, duration, applying user, stdout. Editing a migration after apply flags drift and refuses to silently re-run. The audit trail matches what enterprise change control requires.

The takeaway

Idempotency is an architectural commitment. It has to hold at the database, at the tool surface, at the workflow engine, and at every outbound webhook — or the whole story collapses on the first retry. Actionary makes the commitment in all four places, and enforces it with partial unique indexes, typed conflict clauses, HMAC-signed retries, and a dead-letter ledger the operator can see.

The reasoning is the agent. The planning is a workflow or an agent turn. The landing is durable, retryable, auditable — because the schema, the indexes, and the code refuse to double-execute when the network, the browser, the webhook receiver, or the model tries. That is the substrate an engineering team can bet a five-year roadmap on. More for CTOs.