Install SDK
Use the published npm package on a server-side runtime. Keep DECIDE_API_KEY out of browsers and client bundles.
Developer Examples
These examples are intentionally plain. The production integration should be boring: send context, receive a Decision Record v1 object, route safely, and store the record. Hosted examples below show applications built on Decide without making them dependencies of the core API.
Use this path when a developer wants to prove the integration shape quickly: install the public SDK artifact, run the lifecycle proof pack, verify a packet, then wire one fail-closed boundary before any production mutation.
Use the published npm package on a server-side runtime. Keep DECIDE_API_KEY out of browsers and client bundles.
Generate a Decision Record, execution receipt, Outcome Record, and policy intelligence readout from one action boundary.
Check record, receipt, packet hash, public key registry, and lifecycle links before sharing evidence externally.
Only yes proceeds. no, review, auth errors, rate limits, and runtime failures route to hold or review.
Verified on May 22, 2026 against npm registry metadata: npm latest is 0.1.13 and local source package metadata is 0.1.14 until the next publish.
npm install @decide-fyi/sdk
DECIDE_API_KEY=decide_live_... node ./node_modules/@decide-fyi/sdk/examples/lifecycle-proof-pack.js
npx @decide-fyi/sdk verify-packet decision-packet.json \
--key-registry https://www.decide.fyi/api/decision/receipt-keys \
--summary
const decision = await decide.decide(input, {
idempotencyKey: `billing:${subscriptionId}:discount:${discountId}`,
responseView: "standard"
});
await storeDecisionRecord(decision);
if (decision.verdict === "yes") {
const execution = await applyDiscount();
await decide.recordExecution(decision.decision_id, execution);
return { route: "proceed", decision_id: decision.decision_id };
}
return {
route: "review",
decision_id: decision.decision_id,
reason: decision.verdict || "runtime_hold"
};
Do not treat a copied snippet as production complete until the caller stores the Decision Record, sends an idempotency key, defines hold/review behavior, and verifies exported packets during review.
This is the buyer-readable proof path. A pricing exception becomes a Decision Record, the authorized mutation gets an execution receipt, the result becomes an Outcome Record, and those outcomes feed effectiveness and anomaly review.
/api/decide returns decision_record_v1, action binding, hashes, replay, and verify links.
/execution records target system, mutation, executor, state hashes, and execution_hash.
/outcome links observed metrics and final result back to the original Decision Record.
Effectiveness and anomaly endpoints turn scoped Outcome Records into operational signals.
DECIDE_API_KEY=decide_live_... node sdk/examples/lifecycle-proof-pack.js
{
"lifecycle": "decision_to_execution_to_outcome_to_intelligence",
"decision_record": {
"decision_id": "dec_43b2",
"verdict": "yes",
"record_hash": "7f3a...",
"receipt_hash": "c91e..."
},
"execution_receipt": {
"execution_receipt_id": "exec_71af...",
"execution_hash": "f7a1...",
"action_binding_match": true
},
"outcome_record": {
"outcome_id": "out_9a3c...",
"outcome_hash": "9d1a...",
"outcome_status": "succeeded"
},
"policy_intelligence": {
"effectiveness_score": 0.94,
"effectiveness_recommendation": "healthy",
"anomaly_recommendation": "stable",
"anomaly_count": 0
}
}
Use a server-side key in production. The public playground can test shape, but production calls should originate from infrastructure you control.
curl -i https://www.decide.fyi/api/decide \
-H "content-type: application/json" \
-H "x-api-key: $DECIDE_API_KEY" \
-H "x-idempotency-key: deal_1042_discount_15" \
-d '{
"question": "Approve 15% annual-plan discount exception?",
"mode": "single",
"context": {
"workflow": "pricing_exception",
"source_record_id": "deal_1042",
"requested_action": "approve_discount",
"margin_floor": "passed",
"owner_rule": "verified"
}
}'
{
"decision_record_version": "decision_record_v1",
"c": "yes",
"v": "approved",
"verdict": "yes",
"request_id": "req_9f3c",
"decision_id": "dec_43b2",
"idempotency_key": "deal_1042_discount_15",
"evidence": ["MARGIN_FLOOR_OK", "OWNER_RULE_VERIFIED"],
"evidence_manifest": {
"codes": ["MARGIN_FLOOR_OK", "OWNER_RULE_VERIFIED"],
"sources": []
},
"action": "approve_discount",
"action_binding": {
"system": "pricing_exception",
"resource": "deal_1042",
"proposed_action": "approve_discount"
},
"policy_id": "pricing_exception",
"policy_version": "v3",
"policy_hash": "a94d...",
"policy_bundle": {
"id": "pricing_exception_bundle",
"version": "2026-05-21",
"hash": "b81c...",
"hash_algorithm": "sha256",
"canonicalization": "json.sort_deep.v1",
"source": "git:policies/pricing_exception"
},
"policy_bundle_hash": "b81c...",
"decision_confidence": {
"score": 0.91,
"level": "high",
"similar_decisions": 43,
"policy_stability": "high",
"recommendation": "high_confidence_proceed",
"confidence_hash": "4ca2..."
},
"input_hash": "8b1d...",
"output_hash": "de43...",
"record_hash": "7f3a...",
"receipt_hash": "c91e...",
"receipt_key_id": "decide_ed25519_2026_05",
"receipt_signature": "ed25519:9d4e...",
"receipt_signature_algorithm": "ed25519",
"receipt_public_key_fingerprint": "65af...",
"replay_url": "/api/decision/dec_43b2/replay",
"verify_url": "/api/decision/dec_43b2/verify",
"created_at": "2026-05-19T00:00:00.000Z"
}
Use minimal when the caller only needs to route. Use standard when the caller stores a production record. Use full when the caller wants the complete trust envelope in the first response. All views preserve the same decision_id, record_hash, receipt_hash, policy_bundle_hash when present, decision_confidence, signed receipt fields when configured, and verify_url.
curl -i https://www.decide.fyi/api/decide \
-H "content-type: application/json" \
-H "x-api-key: $DECIDE_API_KEY" \
-d '{
"question": "Approve discount before billing update?",
"response_view": "minimal",
"context": {
"workflow": "billing_discount_gate",
"source_record_id": "sub_1042",
"requested_action": "apply_discount"
}
}'
curl -i https://www.decide.fyi/api/decide \
-H "content-type: application/json" \
-H "x-api-key: $DECIDE_API_KEY" \
-H "Prefer: return=standard" \
-d @pricing_exception.json
Pass one x-idempotency-key per source-system action attempt. A retry with the same canonical payload returns the original Decision Record; reusing the key with a different payload returns 409 and should not execute.
curl -i https://www.decide.fyi/api/decide \
-H "content-type: application/json" \
-H "x-api-key: $DECIDE_API_KEY" \
-H "x-idempotency-key: deal_1042_discount_15" \
-d @pricing_exception.json
# Repeat the same request after a timeout:
# x-idempotency-status: replayed
# body: same decision_id, record_hash, receipt_hash, policy_bundle_hash, and receipt_signature
{
"c": "unclear",
"v": "idempotency_conflict",
"error": "DECIDE_API_IDEMPOTENCY_CONFLICT",
"message": "The same idempotency key was used with a different canonical request payload.",
"idempotency_key": "deal_1042_discount_15",
"decision_id": "dec_43b2",
"input_hash": "new_payload_hash...",
"existing_input_hash": "original_payload_hash..."
}
This helper returns a routing instruction and keeps failure behavior explicit. It does not approve when Decide is unavailable.
export async function decideBeforeAction(payload, { idempotencyKey } = {}) {
const response = await fetch("https://www.decide.fyi/api/decide", {
method: "POST",
headers: {
"content-type": "application/json",
"x-api-key": process.env.DECIDE_API_KEY,
...(idempotencyKey ? { "x-idempotency-key": idempotencyKey } : {})
},
body: JSON.stringify(payload)
});
const data = await response.json().catch(() => ({}));
if (!response.ok) {
return {
route: "review",
reason: "decide_unavailable",
status: response.status,
evidence: data
};
}
if (data.verdict === "yes" || data.c === "yes" || data.v === "approved") return { route: "proceed", data };
if (data.verdict === "no" || data.c === "no" || data.v === "denied") return { route: "block", data };
return { route: "review", data };
}
The npm package includes runnable examples for the common state-changing boundaries. Each example calls Decide, stores the Decision Record, and only performs the mutation when the verdict allows it.
npm install @decide-fyi/sdk
DECIDE_API_KEY=decide_live_... node ./node_modules/@decide-fyi/sdk/examples/billing-discount-gate.js
sdk/examples/pricing-exception.js
sdk/examples/billing-discount-gate.js
sdk/examples/crm-writeback.js
sdk/examples/webhook-queue-gate.js
sdk/examples/agent-action-gate.js
sdk/examples/lifecycle-proof-pack.js
sdk/examples/action-execution-receipt.js
sdk/examples/decision-chain.js
sdk/examples/outcome-tracking.js
sdk/examples/policy-effectiveness.js
sdk/examples/policy-anomalies.js
sdk/examples/policy-benchmarks.js
sdk/examples/policy-confidence.js
sdk/examples/policy-patterns.js
sdk/examples/counterfactual-analysis.js
sdk/examples/github-actions-verify.yml
sdk/fixtures/decision-input.json
sdk/fixtures/valid-decision-record.json
sdk/fixtures/tampered-record.json
sdk/fixtures/replay-diff-example.json
Policy patterns expose first-party request, outcome, and CRM sync shapes for common state-changing workflows. Use them as starting points, then send the final payload through /api/decide before your system mutates state.
curl -i "https://www.decide.fyi/api/decision/policy-patterns?pattern_id=pricing_exception"
const registry = await decide.policyPatterns({ tag: "crm" });
const { policy_pattern: pattern } = await decide.policyPatterns({
patternId: "pricing_exception"
});
const record = await decide.decide(pattern.decision_request_template, {
idempotencyKey: "deal_1042_discount_15",
responseView: "full"
});
Use a short timeout, route errors to review, and persist the response before the calling system changes state.
import os
import requests
def decide_before_action(payload, idempotency_key=None):
headers = {
"content-type": "application/json",
"x-api-key": os.environ["DECIDE_API_KEY"],
}
if idempotency_key:
headers["x-idempotency-key"] = idempotency_key
try:
res = requests.post(
"https://www.decide.fyi/api/decide",
headers=headers,
json=payload,
timeout=10,
)
data = res.json()
except requests.RequestException as exc:
return {"route": "review", "reason": "decide_unavailable", "error": str(exc)}
if not res.ok:
return {"route": "review", "status": res.status_code, "evidence": data}
if data.get("verdict") == "yes" or data.get("c") == "yes" or data.get("v") == "approved":
return {"route": "proceed", "data": data}
if data.get("verdict") == "no" or data.get("c") == "no" or data.get("v") == "denied":
return {"route": "block", "data": data}
return {"route": "review", "data": data}
Use this shape for webhook receivers, queue workers, serverless functions, and internal automations. Decide runs before the mutation.
app.post("/webhooks/discount-requested", async (req, res) => {
const source = req.body;
const payload = {
question: "Approve discount exception before CRM or billing update?",
mode: "single",
context: {
workflow: "pricing_exception",
source_record_id: source.deal_id,
requested_action: "approve_discount",
discount_percent: source.discount_percent,
margin_floor: source.margin_floor_status,
owner_rule: source.owner_rule_status
}
};
const decision = await decideBeforeAction(payload, {
idempotencyKey: `${source.deal_id}:discount:${source.discount_percent}`
});
await saveDecisionRecord(source.deal_id, decision);
if (decision.route === "proceed") {
await applyDiscount(source.deal_id, source.discount_percent, {
decide_request_id: decision.data.request_id,
decide_decision_id: decision.data.decision_id
});
return res.status(200).json({ ok: true, route: "proceed" });
}
await markPendingReview(source.deal_id, decision);
return res.status(202).json({ ok: true, route: decision.route });
});
Verify is the fast tamper check: recompute the record hash, receipt hash, policy bundle hash, and configured receipt signature without rerunning upstream logic. Replay is for QA, disputes, and rollout reviews when you need to compare the original decision record against current declared state.
curl -i https://www.decide.fyi/api/decision/dec_43b2/verify
curl -i https://www.decide.fyi/api/decision/dec_43b2/replay \
-H "content-type: application/json" \
-H "x-api-key: $DECIDE_API_KEY" \
-d '{
"input": {
"question": "Approve 15% annual-plan discount exception?",
"mode": "single",
"context": {
"workflow": "pricing_exception",
"source_record_id": "deal_1042"
}
}
}'
Counterfactuals evaluate labeled scenario inputs against the current Decision API runtime without authorizing downstream mutation. Use them for policy tuning, rollout review, and pricing exception planning.
curl -i https://www.decide.fyi/api/decision/dec_43b2/counterfactuals \
-H "content-type: application/json" \
-H "x-api-key: $DECIDE_API_KEY" \
-d '{
"response_view": "standard",
"scenarios": [
{
"scenario_id": "discount_10_percent",
"label": "10% discount",
"context_patch": {
"discount_percent": 10
}
},
{
"scenario_id": "discount_25_percent",
"label": "25% discount",
"context_patch": {
"discount_percent": 25,
"margin_floor": "review_required"
}
}
]
}'
const report = await decide.counterfactuals(decision.decision_id, {
scenarios: [
{
scenario_id: "discount_10_percent",
context_patch: { discount_percent: 10 }
},
{
scenario_id: "discount_25_percent",
context_patch: {
discount_percent: 25,
margin_floor: "review_required"
}
}
]
});
for (const scenario of report.scenarios) {
console.log(
scenario.scenario_id,
scenario.verdict,
scenario.diff,
scenario.recommendation
);
}
Every stored Decision Record is linked into a caller-scoped audit chain. The Decision Record keeps its stable record_hash; audit_chain binds that hash into a rolling Merkle root with a chain position and link hash.
curl -i "https://www.decide.fyi/api/decision/chains/chain_abc123?limit=10" \
-H "x-api-key: $DECIDE_API_KEY"
const record = await decide.decide(input, { responseView: "full" });
const chain = await decide.decisionChain(record.audit_chain.chain_id, { limit: 10 });
console.log(
chain.chain_size,
chain.merkle_root,
chain.verification.valid
);
After a Decision Record authorizes an action and before you reduce it to a business outcome, record whether the target system executed, queued, skipped, failed, blocked, reviewed, or reverted the mutation. This is the core protocol layer; CRM sync, reference policy remotes, Krafthaus workflows, queues, billing systems, and agents can all report the same decision_execution_v1 shape.
curl -i https://www.decide.fyi/api/decision/dec_43b2/execution \
-H "content-type: application/json" \
-H "x-api-key: $DECIDE_API_KEY" \
-H "x-idempotency-key: deal_1042_discount_15_execution" \
-d '{
"execution_status": "executed",
"action_taken": "approve_discount",
"target_system": "billing",
"target_object_id": "sub_1042",
"mutation": "discount.create",
"execution_id": "billing_run_1042",
"external_ref": "bill_1042",
"decision_record_hash": "7f3a...",
"decision_receipt_hash": "c91e...",
"state_before_hash": "sha256:subscription_before_1042",
"state_after_hash": "sha256:subscription_after_1042"
}'
const { createDecideClient } = require("@decide-fyi/sdk");
const decide = createDecideClient({ apiKey: process.env.DECIDE_API_KEY });
const receipt = await decide.recordExecution(
decision.decision_id,
{
execution_status: "executed",
action_taken: "approve_discount",
target_system: "billing",
target_object_id: "sub_1042",
mutation: "discount.create",
execution_id: "billing_run_1042",
external_ref: "bill_1042",
decision_record_hash: decision.record_hash,
decision_receipt_hash: decision.receipt_hash,
action_binding: decision.action_binding,
state_before_hash: "sha256:subscription_before_1042",
state_after_hash: "sha256:subscription_after_1042"
},
{ idempotencyKey: "deal_1042_discount_15_execution" }
);
console.log(receipt.execution.execution_hash);
After a Decision Record authorizes or routes a proposed action, submit the result with an idempotency key. This creates a hashed decision_outcome_v1 record that can later power effectiveness scoring and anomaly review.
curl -i https://www.decide.fyi/api/decision/dec_43b2/outcome \
-H "content-type: application/json" \
-H "x-api-key: $DECIDE_API_KEY" \
-H "x-idempotency-key: deal_1042_discount_15_outcome" \
-d '{
"outcome_status": "succeeded",
"action_taken": "approve_discount",
"action_executed": true,
"target_system": "billing",
"target_object_id": "sub_1042",
"mutation": "discount.create",
"external_ref": "bill_1042",
"decision_record_hash": "7f3a...",
"decision_receipt_hash": "c91e...",
"observed_metrics": {
"margin_after_discount": 0.182
}
}'
const { createDecideClient } = require("@decide-fyi/sdk");
const decide = createDecideClient({ apiKey: process.env.DECIDE_API_KEY });
await decide.recordOutcome(
decision.decision_id,
{
outcome_status: "succeeded",
action_taken: "approve_discount",
target_system: "billing",
target_object_id: "sub_1042",
mutation: "discount.create",
external_ref: "bill_1042",
decision_record_hash: decision.record_hash,
decision_receipt_hash: decision.receipt_hash
},
{ idempotencyKey: "deal_1042_discount_15_outcome" }
);
After your integration writes Decision Record fields to Salesforce, HubSpot, or another CRM, record a decision_crm_sync_v1 receipt. Decide stores the mapped fields and CRM object reference, not CRM credentials.
curl -i https://www.decide.fyi/api/decision/dec_43b2/crm-sync \
-H "content-type: application/json" \
-H "x-api-key: $DECIDE_API_KEY" \
-H "x-idempotency-key: deal_1042_crm_writeback" \
-d '{
"sync_status": "written",
"sync_direction": "writeback",
"crm_provider": "salesforce",
"crm_object_type": "Opportunity",
"crm_object_id": "006xx000004TmiQAAS",
"crm_record_url": "https://example.my.salesforce.com/006xx000004TmiQAAS",
"decision_record_hash": "7f3a...",
"decision_receipt_hash": "c91e...",
"decision_verdict": "yes",
"decision_action": "approve_discount",
"policy_id": "pricing_exception",
"policy_version": "v3"
}'
const crmSync = await decide.recordCrmSync(
decision.decision_id,
{
sync_status: "written",
crm_provider: "salesforce",
crm_object_type: "Opportunity",
crm_object_id: "006xx000004TmiQAAS",
decision_record_hash: decision.record_hash,
decision_receipt_hash: decision.receipt_hash,
decision_verdict: decision.verdict,
decision_action: decision.action,
policy_id: decision.policy_id,
policy_version: decision.policy_version
},
{ idempotencyKey: "deal_1042_crm_writeback" }
);
console.log(crmSync.crm_sync.sync_id, crmSync.crm_sync.sync_hash);
After outcomes are reported, effectiveness is computed from the latest Outcome Record per decision within the caller's API-key scope. Proxy quality fields are operational signals, not causal ML claims.
curl -i "https://www.decide.fyi/api/decision/policies/pricing_exception/effectiveness?policy_version=v3&limit=1000&min_sample=10" \
-H "x-api-key: $DECIDE_API_KEY"
const effectiveness = await decide.policyEffectiveness("pricing_exception", {
policyVersion: "v3",
limit: 1000,
minSample: 10
});
console.log(
effectiveness.effectiveness_score,
effectiveness.confidence,
effectiveness.recommendation
);
New Decision Records include a decision_confidence_v1 block when caller-scoped Outcome Records are available. You can also inspect the policy baseline directly for a proposed verdict and action.
curl -i "https://www.decide.fyi/api/decision/policies/pricing_exception/confidence?policy_version=v3&verdict=yes&action=approve_discount&limit=1000&min_sample=10" \
-H "x-api-key: $DECIDE_API_KEY"
const confidence = await decide.policyConfidence("pricing_exception", {
policyVersion: "v3",
verdict: "yes",
action: "approve_discount",
limit: 1000,
minSample: 10
});
console.log(
confidence.score,
confidence.level,
confidence.policy_stability,
confidence.recommendation
);
Benchmarks compare caller-scoped outcomes to opt-in anonymized cohort rows only when minimum cohort privacy thresholds are met. The response exposes aggregate percentiles and deltas, not raw customer records.
curl -i "https://www.decide.fyi/api/decision/policies/pricing_exception/benchmarks?policy_version=v3&limit=5000&min_cohort_scopes=3&min_cohort_decisions=30" \
-H "x-api-key: $DECIDE_API_KEY"
const benchmarks = await decide.policyBenchmarks("pricing_exception", {
policyVersion: "v3",
limit: 5000,
minCohortScopes: 3,
minCohortDecisions: 30
});
console.log(
benchmarks.cross_customer.status,
benchmarks.your_metrics.success_rate,
benchmarks.cross_customer.percentiles?.success_rate?.p50,
benchmarks.recommendation
);
Anomaly reports use the same scoped Outcome Records as effectiveness scoring. They return deterministic scores, severity, reason codes, and observed metric baselines; they are operational signals, not model predictions.
curl -i "https://www.decide.fyi/api/decision/policies/pricing_exception/anomalies?policy_version=v3&limit=1000&min_sample=10&threshold=0.35&max_items=10" \
-H "x-api-key: $DECIDE_API_KEY"
const report = await decide.policyAnomalies("pricing_exception", {
policyVersion: "v3",
limit: 1000,
minSample: 10,
threshold: 0.35,
maxItems: 10
});
for (const anomaly of report.anomalies) {
console.log(anomaly.severity, anomaly.anomaly_score, anomaly.reasons);
}
Use the local verifier when a record or packet has left the app that stored it. It recomputes canonical hashes and verifies Ed25519 receipt signatures with the public key registry, the record's public key, or a supplied PEM file.
node scripts/decision-offline-verify.js decision-record.json \
--input decision-input.json \
--key-registry https://www.decide.fyi/api/decision/receipt-keys
node scripts/decision-offline-verify.js decision-record.json \
--input decision-input.json \
--hmac-secret "$DECIDE_RECEIPT_SIGNING_SECRET"
npx @decide-fyi/sdk verify-packet decision-packet.json \
--key-registry https://www.decide.fyi/api/decision/receipt-keys \
--summary
curl -s https://www.decide.fyi/api/decision/receipt-keys
Make the next action explicit in code. The safest production posture is simple: only allowed outcomes proceed.
| Decide result | Caller route | Side effect | Required storage |
|---|---|---|---|
yes / approved | proceed | Run the downstream action. | Store the Decision Record v1 object with record_hash, receipt_hash, verify_url, and replay_url. |
no / denied | block | Do not run the action. | Store the decision record and expose evidence to the owner path. |
review / unclear | review | Hold state until the declared owner reviews. | Store the record and pending owner route. |
401, 403, 429, 5xx | review | Do not approve silently. | Store failure context, source id, and retry or escalation state. |
This is not a required schema. It is a practical starting point for storing the fields that make a decision replayable.
{
"source_system": "crm",
"source_record_id": "deal_1042",
"requested_action": "approve_discount",
"decision_record_version": "decision_record_v1",
"decision_verdict": "yes",
"decide_request_id": "req_9f3c",
"decide_decision_id": "dec_43b2",
"decide_record_hash": "7f3a...",
"decide_receipt_hash": "c91e...",
"decide_policy_bundle_hash": "b81c...",
"decide_receipt_key_id": "decide_ed25519_2026_05",
"decide_receipt_signature": "ed25519:9d4e...",
"decide_receipt_public_key_fingerprint": "65af...",
"decide_replay_url": "/api/decision/dec_43b2/replay",
"decide_verify_url": "/api/decision/dec_43b2/verify",
"decide_policy_version": "v3",
"decide_input_hash": "8b1d...",
"decide_output_hash": "de43...",
"action_binding": {
"system": "pricing_exception",
"resource": "deal_1042",
"proposed_action": "approve_discount"
},
"evidence": ["MARGIN_FLOOR_OK", "OWNER_RULE_VERIFIED"],
"action_status": "executed",
"created_at": "2026-05-19T00:00:00Z"
}
Use these as proof-of-work examples. They are application layers that call or package Decide; a direct Decision API integration does not require adopting them.
Turns a decision question, KPI, options, and guardrails into a packet review and memo workflow powered by Decide.
Presents refund, cancellation, return, and trial checks as product surfaces while stable MCP and REST runtime endpoints remain on Decide.
Shows how a proposed onchain action can become a decision packet with evidence, verdict, packet hash, and operator memo.