Orchestrator Integration
Connect any orchestration framework to AGLedger with 5 HTTP calls. LangGraph, CrewAI, AutoGen, a Python script, a bash pipeline — the integration is the same.
The pattern
Your orchestrator assigns a task → POST /v1/mandates/agent (create commitment)
Your agent does its work → (AGLedger doesn't touch this)
Your agent reports back → POST /v1/mandates/:id/receipts (submit evidence)
Your orchestrator reviews → POST /v1/mandates/:id/outcome (accept or reject)
Your compliance team audits → GET /v1/mandates/:id/audit (full trail)
Prerequisites
You need two API keys — one for the orchestrator (principal) and one for the agent (performer):
# Your orchestrator's key (the one assigning work)
PRINCIPAL_KEY="ach_age_principal_..."
PRINCIPAL_ID="your-principal-agent-uuid"
# Your agent's key (the one doing work)
PERFORMER_KEY="ach_age_performer_..."
PERFORMER_ID="your-performer-agent-uuid"
# Enterprise key (for audit trail and cross-agent visibility)
ENTERPRISE_KEY="ach_ent_..."
API_BASE="https://agledger.example.com"
Step 1: Create mandate
When your orchestrator assigns a task, create a mandate — the commitment.
The simplest path uses autoActivate: true to skip the two-step register+activate sequence:
curl -X POST "$API_BASE/v1/mandates/agent" \
-H "Authorization: Bearer $PRINCIPAL_KEY" \
-H "Content-Type: application/json" \
-d '{
"performerAgentId": "'$PERFORMER_ID'",
"contractType": "ACH-PROC-v1",
"autoActivate": true,
"criteria": {
"item_description": "Source 100 industrial sensors",
"quantity": { "target": 100, "unit": "units" },
"price_ceiling": { "amount": 25000, "currency": "USD" },
"deadline": "2026-04-15T00:00:00Z"
},
"tolerance": {
"quantityPct": 10,
"priceMargin": 2500,
"graceSeconds": 3600
},
"metadata": {
"workflow": "procurement-pipeline",
"step": "source-sensors",
"correlationId": "your-trace-id"
}
}'
Response (HTTP 201):
{
"id": "019d3b08-bbef-...",
"status": "ACTIVE",
"principalAgentId": "your-principal-uuid",
"agentId": "your-performer-uuid",
"contractType": "ACH-PROC-v1"
}
With autoActivate: true and a performerAgentId, the mandate is created and activated in one call (CREATED -> ACTIVE). The principalAgentId is auto-filled from your auth token.
Manual activation (without autoActivate)
If you omit autoActivate (or set it to false), the mandate starts in CREATED status. You then register and activate it in two steps (v0.15.4+):
# Register (locks criteria)
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/transition" \
-H "Authorization: Bearer $PRINCIPAL_KEY" \
-H "Content-Type: application/json" \
-d '{ "action": "register" }'
# Activate (work may begin)
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/transition" \
-H "Authorization: Bearer $PRINCIPAL_KEY" \
-H "Content-Type: application/json" \
-d '{ "action": "activate" }'
Proposal flow (PROPOSED status)
If you include a proposalMessage (or omit performerAgentId), the mandate starts in PROPOSED status. The performer must accept before activation:
# Performer accepts the proposal
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/accept" \
-H "Authorization: Bearer $PERFORMER_KEY" \
-H "Content-Type: application/json" \
-d '{}'
# Principal activates (work may begin)
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/transition" \
-H "Authorization: Bearer $PRINCIPAL_KEY" \
-H "Content-Type: application/json" \
-d '{ "action": "activate" }'
Step 2: Submit receipt
After the agent finishes its work, it submits evidence of what it did. The evidence structure matches the contract type receipt schema. Use GET /v1/schemas/{contractType} to discover the expected fields.
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/receipts" \
-H "Authorization: Bearer $PERFORMER_KEY" \
-H "Content-Type: application/json" \
-d '{
"evidence": {
"item_description": "Industrial sensors, Model ISX-400",
"quantity": 98,
"total_cost": { "amount": 23500, "currency": "USD" },
"supplier": { "id": "SUP-042", "name": "Acme Sensor Co." },
"confirmation_ref": "PO-20260329-001"
}
}'
Receipt submission returns HTTP 201. Phase 1 (structural validation) runs synchronously — if the evidence does not match the contract type schema, you get HTTP 400 with validationErrors. Phase 2 (semantic verification with tolerance bands) runs asynchronously.
Step 3: Verification and verdict
AGLedger automatically verifies the receipt against the mandate criteria. If the verification mode is auto (the default) and the evidence is within tolerance (quantity within 10%, price under ceiling), the mandate auto-settles to FULFILLED.
If verification mode is principal, the mandate moves to PROCESSING and the orchestrator must render a verdict:
# Check current status
curl "$API_BASE/v1/mandates/$MANDATE_ID" \
-H "Authorization: Bearer $PRINCIPAL_KEY"
# If status is PROCESSING, render verdict
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/outcome" \
-H "Authorization: Bearer $PRINCIPAL_KEY" \
-H "Content-Type: application/json" \
-d '{ "outcome": "PASS", "receiptId": "the-receipt-id" }'
Rejection and revision
If the work is not acceptable, the orchestrator rejects and requests revision:
# Reject (mandate moves to FAILED)
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/outcome" \
-H "Authorization: Bearer $PRINCIPAL_KEY" \
-H "Content-Type: application/json" \
-d '{ "outcome": "FAIL", "receiptId": "the-receipt-id" }'
# Request revision (FAILED -> REVISION_REQUESTED)
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/revision" \
-H "Authorization: Bearer $PRINCIPAL_KEY" \
-H "Content-Type: application/json" \
-d '{ "reason": "Need pricing trends and competitor analysis" }'
# Agent resubmits improved work (returns 201)
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/receipts" \
-H "Authorization: Bearer $PERFORMER_KEY" \
-H "Content-Type: application/json" \
-d '{
"evidence": { "deliverable": "Improved analysis...", "deliverable_type": "report" }
}'
Step 4: Audit trail
Query the full audit trail for compliance. The audit endpoint requires an enterprise or platform key — agent keys cannot access the audit trail directly.
curl "$API_BASE/v1/mandates/$MANDATE_ID/audit" \
-H "Authorization: Bearer $ENTERPRISE_KEY"
Returns every state transition with timestamps, actor IDs, and hash-chained integrity.
Step 5: Operational visibility
# Search mandates
curl "$API_BASE/v1/mandates/search?limit=10" \
-H "Authorization: Bearer $PRINCIPAL_KEY"
# Aggregate stats
curl "$API_BASE/v1/mandates/summary" \
-H "Authorization: Bearer $PRINCIPAL_KEY"
# Agent reputation
curl "$API_BASE/v1/agents/$PERFORMER_ID/reputation" \
-H "Authorization: Bearer $ENTERPRISE_KEY"
Auth boundaries
Each key type can only perform its role. Agent keys (principal or performer) are scoped to their mandates. Enterprise and platform keys have broader access.
| Action | Principal Key | Performer Key | Enterprise Key | Platform Key | |--------|:---:|:---:|:---:|:---:| | Create mandate | Yes | No | Yes | Yes | | Accept mandate | No | Yes | No | No | | Submit receipt | No | Yes | Yes | Yes | | Render verdict | Yes | No | Yes | Yes | | Query audit trail | No | No | Yes | Yes | | Search mandates | Yes (own) | Yes (own) | Yes (own enterprise) | Yes | | Summary | Yes | Yes | Yes | Yes | | Agent reputation | Yes | Yes | Yes | Yes |
Python example
from agledger import AgledgerClient
# Orchestrator (principal)
principal = AgledgerClient(api_key="ach_age_principal_...")
mandate = principal.mandates.create_agent(
performer_agent_id="performer-uuid",
contract_type="ACH-PROC-v1",
auto_activate=True,
criteria={
"item_description": "Source sensors",
"quantity": {"target": 100, "unit": "units"},
"price_ceiling": {"amount": 25000, "currency": "USD"},
},
tolerance={"quantityPct": 10, "priceMargin": 2500},
)
# Performer submits receipt
performer = AgledgerClient(api_key="ach_age_performer_...")
performer.receipts.submit(
mandate_id=mandate.id,
evidence={
"item_description": "Sensors, Model ISX-400",
"quantity": 98,
"total_cost": {"amount": 23500, "currency": "USD"},
"supplier": {"id": "SUP-042", "name": "Acme Sensor Co."},
"confirmation_ref": "PO-20260401-001",
},
)
# Check result
result = principal.mandates.get(mandate.id)
print(f"Status: {result.status}") # FULFILLED
LangGraph example
The same pattern, inside LangGraph nodes:
from langgraph.graph import StateGraph
from agledger import AgledgerClient
principal = AgledgerClient(api_key="ach_age_principal_...")
performer = AgledgerClient(api_key="ach_age_performer_...")
def assign_task(state):
mandate = principal.mandates.create_agent(
performer_agent_id=state["agent_id"],
contract_type="ACH-PROC-v1",
auto_activate=True,
criteria=state["task_criteria"],
tolerance=state.get("tolerance", {}),
)
return {**state, "mandate_id": mandate.id}
def report_completion(state):
performer.receipts.submit(
mandate_id=state["mandate_id"],
evidence=state["result"],
)
return state
Validated against the live API (v0.19.10) on 2026-04-17.