DeAlgoOpen workspace
Looking for the agent financial controls API?

This reference describes the separate core decision API. Restricted commerce connections use the agent quickstart and commerce OpenAPI reference.

Reference

Core API reference.

Constitutional Execution Infrastructure for Autonomous Systems — API reference. DeAlgo evaluates every action your agents propose (against history, live conditions, policy, and replay-frozen substrate state) and returns APPROVE, DELAY, or DENY before irreversible execution. Verdicts chain into an offline-verifiable SSI Protocol™ ledger; outcomes refine future decisions; every finality verdict requires constitutional convergence.

Just want to run something? First decision in 60 seconds →

Without / With

DeAlgo currently exposes a programmable decision affordance: one extra check before any consequential action — and you get a reason back when the substrate refuses:

# Without DeAlgo
execute_trade(signal)                   # blindly executes

# With DeAlgo
verdict = dealgo.decide(signal)
if verdict.decision == "APPROVE":
    execute_trade(signal)
else:
    log(verdict.reason)                 # know why you didn't act

30-second example

Set DEALGO_API_KEY to your bearer token and run:

curl -X POST https://dealgo-portal.vercel.app/api/v1/decide \
  -H "Authorization: Bearer $DEALGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action_type": "send_email",
    "risk_score": 0.1,
    "confidence": 0.95,
    "intent": "reply to customer with quote",
    "metadata": { "agent_id": "support-bot-7" }
  }'

Response:

{
  "decision":             "APPROVE",
  "action":               "EXECUTE",
  "reason":               "Action approved.",
  "reason_raw":           "permitted",
  "retry_after_ms":       null,
  "stimulus_id":          "ec9262c8-79e1-4665-…",
  "latency_ms":           41,
  "environment":          "test",
  "idempotency_replayed": false,
  "decision_log_id":      "cmoi2g2yzze09y0xww82kb6z8",
  "v2_bias":              0
}

v2_bias is an internal signal used by DeAlgo's v2 evaluation layer; safe to ignore for standard integrations.

For TypeScript, Python SDK, and MCP equivalents see Quickstart. Or install the SDK directly: pip install dealgo-csc.

Authentication

All requests use a Bearer token. Generate keys on the Keys page. Keys are shown once; DeAlgo stores only the SHA-256 hash plus an indexed lookup suffix.

Authorization: Bearer dealgo_sk_live_<32 chars>   // production
Authorization: Bearer dealgo_sk_test_<32 chars>   // sandbox — see /environments below

POST /v1/decide

Submit one proposed action. DeAlgo runs it through the runtime evaluation and returns a decision.

Request

{
  "action_type": "send_email",     // required — what kind of action
  "risk_score":  0.3,              // [0..1] caller-assessed risk
  "confidence":  0.95,             // [0..1] caller-assessed confidence
  "intent":      "reply to customer with quote",
  "metadata":    {                 // free-form pass-through
    "agent_id":   "support-bot-7",
    // Trading callers: pass domain context here for validation
    "symbol":     "BTCUSD",
    "volatility": 0.18
  }
}

Response

{
  "decision":             "APPROVE" | "DELAY" | "DENY",
  "action":               "EXECUTE" | "WAIT" | "REJECT",
  "reason":               "human-readable explanation",
  "reason_raw":           "engine verdict string for audit",
  "retry_after_ms":       5000 | null,        // present on DELAY
  "stimulus_id":          "ec9262c8-…",       // engine-side id
  "latency_ms":           41,
  "environment":          "live" | "test",
  "idempotency_replayed": false,
  "decision_log_id":      "cmoi…"             // open /decisions/<id> for full trace
}

decision_log_id is your direct link to the trace inspector at /decisions/<id> — every step the runtime evaluated, with a cryptographic chain proof.

POST /v1/outcomes

Close the loop on a previous decision. Outcomes are signed (ed25519) and stored on a separate, append-only chain. They do not retroactively modify any audit row.

Request

{
  "stimulus_id":         "ec9262c8-…",       // from /v1/decide response
  "magnitude":           0.7,                // [-1..1]: harm vs benefit
  "confidence":          0.95,               // [0..1]
  "signature":           "<base64 ed25519>", // over the canonical bytes
  "public_key":          "<base64>",         // matches a registered key
  "source":              "support-bot-7",
  "context_type":        "General Stimulus",
  "outcome_observed_at": "2026-04-30T12:00:00Z",
  "is_final":            true
}

The simplest path is the SDK: client.report_outcome(...) handles the field shape; you only have to provide the signature.

Live vs sandbox keys

Keys come in two flavors. Both write to the same audit chain — the difference is metering and posture.

  • sk_live_ — production. Counts toward your monthly quota. environment: "live" on every response.
  • sk_test_ — sandbox. Skips metering entirely. Otherwise identical: same gates, same chain, same trace inspector. environment: "test" on every response.

Toggle key class on the Keys page, or via the environment field in POST /api/keys. Use sandbox keys in CI, demos, and the /playground.

Idempotency

Send the same Idempotency-Key header on a retry to avoid double-metering and double-writing.

POST /v1/decide
Authorization: Bearer dealgo_sk_test_…
Idempotency-Key: my-request-uuid

→  HTTP 200
   { "idempotency_replayed": false, "decision_log_id": "abc1", … }

# retry within 24h with the same key:
→  HTTP 200
   { "idempotency_replayed": true,  "decision_log_id": "abc1", … }   // same row

Window: 24 hours, scoped per API key. Backed by @@unique([apiKeyId, idempotencyKey]) on the DecisionLog table — concurrent retries with the same key serialize to one row. Different keys for the same payload produce two rows.

Rate limits

Default limit: 60 req/min per API key. Returns HTTP 429 with Retry-After on overflow. Every response (including non-429) carries:

X-RateLimit-Limit:     60
X-RateLimit-Remaining: 47
X-RateLimit-Reset:     38   // seconds until the current minute window rolls

Use Idempotency-Key on retries — a 429 followed by a same-key replay won't double-meter once you back off and the window rolls.

Errors

401unauthorizedmissing or malformed Authorization header.
403revokedkey was revoked, or not bound to a workspace.
422validation_failedpayload invalid (bad type, out-of-range value, missing required field).
429rate_limitedover 60 req/min per key. Retry-After seconds set.
502upstream_errorruntime engine unreachable; safe to retry with backoff.
503chain_integrity_violationaudit-chain integrity check failed at boot. Refusing new writes until resolved.

All v1 errors return a consistent envelope: { error: { code, message, details? } }.

Decision semantics

  • APPROVE / EXECUTE — proceed.
  • DELAY / WAIT — pacing applied. Retry after retry_after_ms (use the same Idempotency-Key).
  • DENY / REJECT — validation or policy violation. Do not retry; investigate and surface reason to the user.

Handling the decision

These behaviors are required for correct integration. Do not improvise.

Each decision has a single correct caller behavior. Branch on verdict.decision and apply the matching block.

# APPROVE — execute the action
verdict = dealgo.decide(signal)
if verdict.decision == "APPROVE":
    execute(signal)

# DELAY — wait and retry with the SAME idempotency key
elif verdict.decision == "DELAY":
    time.sleep(verdict.retry_after_ms / 1000)
    # Same Idempotency-Key prevents duplicate decisions and double-counting.
    retry(signal, idempotency_key=original_key)

# DENY — interrupt, do not retry
elif verdict.decision == "DENY":
    raise BlockedDecision(verdict.reason)  # explicit, impossible to ignore

Combined dispatcher — one function that handles all three branches:

def gated(signal, idempotency_key):
    v = dealgo.decide(signal, idempotency_key=idempotency_key)
    if v.decision == "APPROVE":
        return execute(signal)
    if v.decision == "DELAY":
        time.sleep(v.retry_after_ms / 1000)
        # Safe: same Idempotency-Key prevents duplicate decisions on retry.
        return gated(signal, idempotency_key)
    raise BlockedDecision(v.reason)

BlockedDecision is a caller-defined exception type. Raising on DENY makes the block explicit and forces the calling code to handle it — silent return Noneis the wrong default.

Logging & audit

Every call is recorded with the input, normalized decision, raw runtime response, source attribution, and latency. The dashboard surfaces recent activity; the full log is retained per your plan. Click any row in /decisions to inspect the full decision trace and chain proof for that decision.

Audit chain (SSI Protocol™)

DeAlgo's audit log is structured as a hash chain conforming to SSI Protocol™. Each decision row carries previousHash, chainHash, and chainScope; the chain is per workspace.

chainHash[i] = sha256_hex( (rows[i-1].chainHash ?? "") + "\n" + canonical(rows[i]) )

canonical(row) is JSON-encoded with sorted keys over a fixed field set (id, userId, apiKeyId, workspaceId, projectId, source, inputPayload, decision, action, reason, v2Bias, latencyMs, rawResponse, createdAt). The format is frozen — any change would break verification of every row written before the change.

GET /api/audit/verify

Returns the full chain for the calling key's workspace, with each row's canonical bytes inlined. A third party can recompute every chainHash from the published bytes and confirm the chain is intact, without trusting our database.

GET /api/audit/verify[?from=<id>&to=<id>]
Authorization: Bearer dealgo_sk_live_…

→ {
    "scope": "workspace:cuid…",
    "ok": true,
    "broke_at_id": null,
    "verified_count": 248,
    "rows": [
      { "id": "…", "createdAt": "…",
        "previousHash": "…", "chainHash": "…",
        "canonical": "{\"action\":\"EXECUTE\",…}" }
    ]
  }

On any chain break, ok is false and broke_at_id identifies the first row whose hash fails to reproduce. DeAlgo also performs this check at server boot; a break flips the runtime fail-closed (HTTP 503 on /v1/decide) until investigated.

Verify the chain yourself

Each row in the response includes a raw field with the underlying SIGNED_FIELDS values, so you (or your auditor) can re-canonicalize independently and not trust our server-side encoding. The response also carries an export_hash over the concatenated canonical bytes, which detects tampering of the JSON file itself in transit.

Two-liner:

curl -s https://dealgo-portal.vercel.app/api/audit/verify \
  -H "Authorization: Bearer $DEALGO_API_KEY" > chain.json

python verify_chain.py chain.json
# → OK — verified 248 rows
#     export_hash: 9f3a…cc1d (matches)

verify_chain.py is ~80 lines, stdlib only, lives in the public source at scripts/verify_chain.py. It re-canonicalizes from row.raw with its own code — so the verification does not depend on a single line of our server.

Honest scope: this proves the chain is internally consistent and that the JSON file you received wasn't tampered with in transit. It does not yet prove the chain wasn't rewritten on our server before you exported it. External time anchoring (publishing the latest chainHash to a public timestamp authority) is a separate phase.

Governance lifecycle

Every stage from runtime decision to offline proof. Each stage is recorded in a chain or system surface that's independently inspectable; conditional stages only fire on certain decisions. This is the platform mental model — the same diagram appears on/ssiand inside cross-chain receipts.

  1. AI Decision

    Decision chain

    The runtime evaluates the stimulus and emits APPROVE / DELAY / DENY / ESCALATE.

  2. Policy Evaluation

    Decision chain · nlpe_shadow

    Workspace policies (max_value_limit, require_approval, time_restriction, rate_limit, stop_after_condition) refine the decision.

  3. Approval / Escalation

    Approval table

    ESCALATE verdicts open an Approval row pending human review.

  4. Operator Action

    Operator chain

    Human approves / denies — or edits a policy, rotates a key, runs a recovery script.

  5. Execution · Webhook

    WebhookDelivery table

    Resolution fires a signed webhook to the customer's worker. DeAlgo never executes — the customer's system does.

  6. Outcome Ingestion

    Outcome chain (brain) + DecisionLog.outcomeAttached (sidecar)

    Customer's worker reports the real-world outcome via signed POST /v1/outcomes.

  7. Evidence Attachment

    outcomeAttached.metadata.evidence[]

    Outcome metadata can carry external evidence links (Stripe receipts, GitHub PRs, dashboard URLs).

  8. Cross-Chain Receipt

    /api/audit/receipt/[id]

    All chains for one decision composed into a single deterministic bundle with a top-level bundle_hash.

  9. Offline Verification

    scripts/verify-receipt.mts · in-browser SubtleCrypto

    Recipient reproduces every chain hash + bundle hash with SHA-256 alone. No DeAlgo dependency.

  10. Incident · Remediation

    /incidents · /trust · operator chain

    If integrity ever breaks, the workspace fail-closes. Mutation is localized byte-exact, repaired deterministically, recorded on the operator chain.

Stages with a ring are conditional — they only fire on certain verdicts. Color groups: decision/policy · human governance · execution · outcome/evidence · portable proof · incident response.

The discipline rule the architecture follows: each chain is append-only, byte-verifiable, and never recomputed to "fix" a break. When integrity ever fails, the workspace fail-closes (HTTP 503 on /v1/decide) until the originally-signed bytes are restored. See /trust for an unredacted record of every detected platform-level integrity event.

Operator action chain

A second cryptographic chain alongside the decision chain, scoped per workspace, recording every privileged human action. The decision chain proves what the AI did; the operator chain proves what humans did.

Same canonical-bytes / chainHash discipline as the decision chain. Each row carries (action, resourceType, resourceId, before, after, actorLabel) plus the chain link (previousHash, chainHash, chainScope).chainScope = "operator:<workspaceId>".

What v1 records

  • policy.create · policy.update · policy.toggle_enabled · policy.delete
  • approval.resolve (the afterJson.status field carries approve/deny)

Future wire-ins (agent.rename, key.revoke, webhook.update, incident.recovery_run, etc.) compose onto the same primitive — see lib/operatorAction.ts for the helper signature.

Where to see it

The Operator Log surface lives at /audit/actions (under Operate) — a paginated, filterable index. Each row links to a detail page with side-by-side before/after JSON diff, chain link, and a Verify this row in your browser button that recomputes chainHash with SubtleCrypto.

Discipline guarantees

  • Append-only. Operator action rows are never UPDATEd or DELETEd post-commit. Same SIGNED_FIELDS rule as the decision chain — mutating row bytes invalidates the chain.
  • Same-transaction coupling for pure-local mutations. Policy CRUD + chain insert run in one Prisma transaction. If the chain insert fails, the policy edit rolls back.
  • Secrets masked at the writer. A sensitive-key detector replaces values whose key matches /secret|password|token|api[_-]?key|private[_-]?key|authorization|bearer/i with "***" before persistence. 14-assertion conformance suite at scripts/test-mask.mts.
  • Boot integrity walk. Every operator chain is re-walked at server boot; a break fail-closes the workspace.

What v1 omits: a Bearer-authed API for operator mutations (no CLI binary today; policy edits and approval resolutions are session-only). Tracked as a deferred entry in the protocol unlock roadmap.

Cross-chain governance receipts

A single deterministic bundle for one decision that composes every relevant chain row into one portable artifact. The recipient can reproduce every cryptographic claim inside it offline using SHA-256 alone — no DeAlgo dependency, no DB connection, no API call.

This is the portability property the SSI Protocol has always claimed, made operationally demonstrable.

What's in a receipt

For one decisionLogId:

  • The decision row + its canonical_bytes + chain link.
  • The approval row (if the decision escalated).
  • Every WebhookDelivery attempt for that approval.
  • The outcome receipt (sidecar or legacy fallback).
  • Every operator action linked to this decision or its approval, each with its own canonical bytes + chain link.
  • A top-level bundle_hash over the canonical-JSON form of everything above.

Field-by-field spec: docs/spec/RECEIPT_BUNDLE.md in the portal repo. v1 is unsigned at the bundle level — v2 will attach a platform signature over bundle_hash once the producer-key registry ships.

Three ways to verify

  1. In-browser, one click. On any decision page click 📜 Receipt in the header to open the viewer at /audit/receipt/[id]. Press Verify in browser — every chain hash + the bundle hash recompute via SubtleCrypto with no server round-trip. The strongest demonstration of the no-trust property; takes about 2 seconds.
  2. Download + offline verify with the reference verifier.
    # In the viewer, click "⬇ Download JSON"
    # Then locally:
    git clone https://github.com/dealgo-systems/dealgo-portal
    cd dealgo-portal
    npx tsx scripts/verify-receipt.mts ./receipt-<id>.json
    
    # Output:
    #   1. Decision row              ✓ decision chain_hash reproduces
    #   2. Operator actions          (N rows all ✓)
    #   3. Bundle hash               ✓ bundle_hash reproduces from contents
    #   ✓ RECEIPT VERIFIED — all chains + bundle hash check out
    The verifier is pure Node + Node's built-in crypto — zero DeAlgo dependencies, no database, no network call after the receipt is on disk.
  3. Programmatic GET. Any session-authed call to /api/audit/receipt/[id] returns the bundle as JSON with Content-Disposition: attachment. Suitable for SDK integration or compliance pipelines.

Why no-trust verification matters

A vendor that says trust our integrity claims is asking you to trust the vendor. A vendor that publishes the canonical bytes and the hash formula is asking you to trust the math. Those are very different positions in an audit.

Receipts compose into evidence packets, regulator submissions, legal-discovery bundles, and customer SIEM egress as they emerge — each of those derives from this primitive without DeAlgo inventing new trust surfaces.

What v1 deliberately omits

  • Top-level platform signature over bundle_hash — pending the producer-key registry. Bundle shape stays compatible.
  • Producer signature inlined on the outcome — fetched separately from the brain's outcome chain.
  • Frozen policy snapshot at decision time — pending brain-side change.
  • Multi-decision bundles. v1 is one decision, one receipt.