Connecting Enterprise Systems
Mixed chains: deterministic enterprise apps + AI agents, one accountability layer.
No enterprise runs pure AI-agent chains. Real workflows mix AI agents with Salesforce, NetSuite, SAP, billing systems, and internal APIs. AGLedger handles the full chain — the mandate lifecycle is the same regardless of whether the caller is an AI agent, an ERP system, or a human clicking a button.
The real-world chain
Salesforce (creates PO) → AI Agent (sources vendor) → NetSuite (books payment)
principal performer performer
deterministic non-deterministic deterministic
Each system creates or fulfills mandates using standard HTTP calls. AGLedger does not know or care what is behind the API key.
Pattern 1: ERP assigns, AI agent delivers
Your ERP system creates a mandate with exact specifications. Your AI agent fulfills it with whatever it finds. Your ERP renders the verdict using its own business rules.
ERP creates mandate
curl -X POST "$API_BASE/v1/mandates/agent" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"performerAgentId": "'$AI_AGENT_ID'",
"contractType": "ACH-PROC-v1",
"autoActivate": true,
"criteria": {
"item_description": "Industrial pressure sensors, Model PS-200, ISO 9001",
"quantity": { "target": 200, "unit": "units" },
"price_ceiling": { "amount": 50000, "currency": "USD" },
"deadline": "2026-04-05T00:00:00Z"
},
"tolerance": { "quantityPct": 5, "priceMargin": 2500, "graceSeconds": 86400 },
"verificationMode": "principal",
"metadata": {
"source": "erp-system",
"poNumber": "PO-2026-04821",
"department": "manufacturing",
"budgetCode": "MFG-2026-Q2"
}
}'
Key points:
verificationMode: "principal"means the ERP renders the verdict, not AGLedger's tolerance engine.autoActivate: truecreates and activates the mandate in a single request (no separate register/activate calls). The audit trail records all internal transitions.principalAgentIdis optional — auto-filled from the authenticated agent key.
AI agent submits receipt
The agent found a different (better) model from a different supplier at a different price:
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/receipts" \
-H "Authorization: Bearer $AI_AGENT_KEY" \
-H "Content-Type: application/json" \
-d '{
"evidence": {
"item_description": "Pressure sensors, Model PS-200X (upgraded), ISO 9001 + ISO 14001",
"quantity": 195,
"total_cost": { "amount": 47500, "currency": "USD" },
"supplier": { "id": "SUP-GLOBAL-019", "name": "GlobalSensor Ltd." },
"confirmation_ref": "GS-20260329-001"
}
}'
ERP renders verdict
Your ERP applies its own logic — not AGLedger's tolerance engine:
# Your ERP's verdict logic
quantity_ok = 195 >= 200 * 0.95 # within 5%? Yes
price_ok = 47500 <= 50000 # under ceiling? Yes
verdict = "PASS" if (quantity_ok and price_ok) else "FAIL"
# Get the receipt ID first
RECEIPT_ID=$(curl -s "$API_BASE/v1/mandates/$MANDATE_ID/receipts" \
-H "Authorization: Bearer $ERP_KEY" | jq -r '.data[0].id')
# Render verdict
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/outcome" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{ "outcome": "PASS", "receiptId": "'$RECEIPT_ID'" }'
Pattern 2: Downstream chain trigger
The AI agent's completed procurement triggers a billing workflow in your payment system:
# Billing system creates downstream mandate
# Metadata links it to the upstream procurement
curl -X POST "$API_BASE/v1/mandates" \
-H "Authorization: Bearer $BILLING_KEY" \
-H "Content-Type: application/json" \
-d '{
"enterpriseId": "'$ENTERPRISE_ID'",
"contractType": "ACH-TXN-v1",
"contractVersion": "1",
"platform": "billing-system",
"agentId": "'$BILLING_AGENT_ID'",
"criteria": {
"description": "Process payment: $47,500 to GlobalSensor Ltd."
},
"tolerance": {},
"metadata": {
"source": "billing-system",
"upstreamMandateId": "'$PROCUREMENT_MANDATE_ID'",
"poNumber": "PO-2026-04821",
"vendor": "GlobalSensor Ltd.",
"amount": 47500
}
}'
The billing system activates, submits receipt, and the mandate auto-settles:
# Register + activate (enterprise flow)
curl -X POST "$API_BASE/v1/mandates/$BILLING_MANDATE_ID/transition" \
-H "Authorization: Bearer $BILLING_KEY" \
-H "Content-Type: application/json" \
-d '{ "action": "register" }'
curl -X POST "$API_BASE/v1/mandates/$BILLING_MANDATE_ID/transition" \
-H "Authorization: Bearer $BILLING_KEY" \
-H "Content-Type: application/json" \
-d '{ "action": "activate" }'
# Billing system submits deterministic receipt
curl -X POST "$API_BASE/v1/mandates/$BILLING_MANDATE_ID/receipts" \
-H "Authorization: Bearer $BILLING_KEY" \
-H "Content-Type: application/json" \
-d '{
"evidence": {
"confirmations": [{
"ref": "TXN-20260329-001",
"type": "wire_transfer",
"provider": "chase-commercial",
"cost": { "amount": 47500, "currency": "USD" }
}]
}
}'
Retry safety (idempotency)
Deterministic systems have retry logic. If a receipt submission times out, the app retries.
AGLedger handles this safely:
- Duplicate receipt on an already-fulfilled mandate returns 422 (not 500)
- The error message is clear: "mandate not in a state that accepts receipts"
- Your retry logic can treat 422 as "already processed" and move on
First attempt: 201 Created → receipt accepted
Retry attempt: 422 Unprocessable → mandate already moved past ACTIVE
Linking chains
Two options for connecting mandates across a chain:
dependsOn(first-class): informational dependency references to other mandate IDs. Recorded in the audit trail for traceability. No activation gating — both mandates proceed independently.metadata.upstreamMandateId(convention): soft reference for audit correlation. No enforcement.
The examples above use the metadata convention. To use dependsOn instead, add "dependsOn": ["upstream-mandate-id"] to the creation payload.
Chain audit
Both mandates have independent audit trails. The metadata links them:
# Procurement audit trail
curl "$API_BASE/v1/mandates/$PROCUREMENT_MANDATE_ID/audit" \
-H "Authorization: Bearer $ERP_KEY"
# Billing audit trail
curl "$API_BASE/v1/mandates/$BILLING_MANDATE_ID/audit" \
-H "Authorization: Bearer $BILLING_KEY"
# Billing mandate references upstream via metadata
curl "$API_BASE/v1/mandates/$BILLING_MANDATE_ID" \
-H "Authorization: Bearer $BILLING_KEY"
# → metadata.upstreamMandateId = the procurement mandate ID
Error handling
Enterprise apps do not self-correct from ambiguous errors. AGLedger returns RFC 9457 problem details:
{
"type": "/problems/validation-error",
"title": "Validation Error",
"status": 400,
"detail": "body must have required property 'criteria'",
"instance": "/v1/mandates",
"errors": [{ "instancePath": "", "keyword": "required", "params": { "missingProperty": "criteria" } }]
}
Your integration can parse status, detail, and errors[] programmatically. No ambiguity.
Actor types
| Actor Type | Integration | Key Difference | |---|---|---| | AI Agent | SDK or HTTP. Variable payloads. May self-correct from 422 errors. | Non-deterministic — needs schema discovery | | Enterprise App | HTTP. Fixed payloads. Retry logic. Error codes. | Deterministic — breaks on schema changes, never hallucinates | | Human | Dashboard (future) or API. | Slow, but the ultimate principal |
AGLedger treats them all the same. A mandate is a mandate. The accountability question is identical: "did the thing happen, within bounds, on time?"
See also
- Entity References — Link mandates to external ERP records (purchase orders, invoices, tickets) using the
referencesarray on mandate creation. This is the structured alternative tometadatafor cross-system traceability. Guide forthcoming (cross-repo issue #2).
Validated with 18 assertions against the live v0.19.10 API on 2026-04-17.