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:

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:

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:

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


Validated with 18 assertions against the live v0.19.10 API on 2026-04-17.