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 act30-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
Retry-After seconds set.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 sameIdempotency-Key). - DENY / REJECT — validation or policy violation. Do not retry; investigate and surface
reasonto 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 ignoreCombined 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.
AI Decision
Decision chainThe runtime evaluates the stimulus and emits APPROVE / DELAY / DENY / ESCALATE.
Policy Evaluation
Decision chain · nlpe_shadowWorkspace policies (max_value_limit, require_approval, time_restriction, rate_limit, stop_after_condition) refine the decision.
Approval / Escalation
Approval tableESCALATE verdicts open an Approval row pending human review.
Operator Action
Operator chainHuman approves / denies — or edits a policy, rotates a key, runs a recovery script.
Execution · Webhook
WebhookDelivery tableResolution fires a signed webhook to the customer's worker. DeAlgo never executes — the customer's system does.
Outcome Ingestion
Outcome chain (brain) + DecisionLog.outcomeAttached (sidecar)Customer's worker reports the real-world outcome via signed POST /v1/outcomes.
Evidence Attachment
outcomeAttached.metadata.evidence[]Outcome metadata can carry external evidence links (Stripe receipts, GitHub PRs, dashboard URLs).
Cross-Chain Receipt
/api/audit/receipt/[id]All chains for one decision composed into a single deterministic bundle with a top-level bundle_hash.
Offline Verification
scripts/verify-receipt.mts · in-browser SubtleCryptoRecipient reproduces every chain hash + bundle hash with SHA-256 alone. No DeAlgo dependency.
Incident · Remediation
/incidents · /trust · operator chainIf integrity ever breaks, the workspace fail-closes. Mutation is localized byte-exact, repaired deterministically, recorded on the operator chain.
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.deleteapproval.resolve(theafterJson.statusfield 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/iwith"***"before persistence. 14-assertion conformance suite atscripts/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
WebhookDeliveryattempt 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_hashover 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
- In-browser, one click. On any decision page click
📜 Receiptin 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. - 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-incrypto— zero DeAlgo dependencies, no database, no network call after the receipt is on disk. - Programmatic GET. Any session-authed call to
/api/audit/receipt/[id]returns the bundle as JSON withContent-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.