Parameter binding.
OAuth confirms who an agent is. SPIFFE confirms its workload identity. Microsoft AGT confirms it can call transfer_funds. None of them confirm what parameters it passes. Parameter binding closes that gap.
Define a parameter binding
Add a param_binding condition to your pact. The condition carries a single parameterBinding object with a tool name and an array of rules.
{
"name": "wire-transfer guardrails",
"agentId": "YOUR_AGENT_UUID",
"conditions": [
{
"type": "param_binding",
"operator": "eq",
"severity": "critical",
"verificationMethod": "deterministic",
"value": null,
"description": "Constraints on transfer_funds parameters",
"parameterBinding": {
"tool": "transfer_funds",
"rules": [
{
"paramPath": "destination",
"allowList": ["0xAB12...", "0xCD34..."],
"required": true
},
{
"paramPath": "amount",
"valueRange": { "min": 1, "max": 100000 }
},
{
"paramPath": "memo",
"regex": "^[\\w\\s-]{0,128}$"
}
]
}
}
]
}Rule constraints
One rule per parameter path. Any combination of constraints is allowed; all violations are reported with the condition's severity.
allowListValue (coerced to string) must be present in this list. Exact-match. Use for allow-listed destinations, recipients, tool subjects.
denyListValue must NOT be present. Use for blocklists or known-bad recipients.
regexValue (coerced to string) must match the supplied regex. Use for shape constraints — memo formats, identifier shapes, language constraints.
valueRangeValue must be numeric and within [min, max] inclusive. Either bound may be omitted for an open-ended range.
maxAmountValue must be numeric and not exceed amount. Currency is metadata for the audit log. Use for monetary caps on transfers, refunds, payouts.
requiredWhen true, an absent parameter is a violation. Default false. Useful for ensuring an audit context is always passed.
Validate a call
POST the agent's attempted tool call to /api/v1/pacts/[pactId]/validate-call and receive a structured verdict.
curl -X POST https://www.armalo.ai/api/v1/pacts/${PACT_ID}/validate-call \
-H "X-Pact-Key: ${ARMALO_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"tool": "transfer_funds",
"params": {
"destination": "0xUNKNOWN",
"amount": 5000000,
"memo": "ok"
},
"sessionId": "sess_abc"
}'Example response — two violations, both critical:
{
"valid": false,
"pactId": "11111111-1111-1111-1111-111111111111",
"tool": "transfer_funds",
"bindingsConsidered": 1,
"severityHighest": "critical",
"violations": [
{
"rule": "allow_list",
"paramPath": "destination",
"observedValue": "0xUNKNOWN",
"reason": "Parameter 'destination' value '0xUNKNOWN' is not in the allow-list of 2 entries.",
"severity": "critical"
},
{
"rule": "value_range",
"paramPath": "amount",
"observedValue": 5000000,
"reason": "Parameter 'amount' value 5000000 exceeds maximum 100000.",
"severity": "critical"
}
]
}Continuous validation via telemetry
Instead of pre-validating each call, you can stream every call to the telemetry ingest with the pact attached. The server evaluates the binding in continuous time and records violations to the room ledger automatically.
tel.toolCall({
sessionId,
agentId,
tool: 'transfer_funds',
params: { destination: '0xUNKNOWN', amount: 5_000_000 },
outcome: 'success',
attemptedAt: new Date().toISOString(),
pactId: 'YOUR_PACT_UUID', // ← enables continuous param-binding eval
});Every validate-call writes an audit log entry — pact.call_validated on pass, pact.call_rejected on any violation — with tool, sessionId, severityHighest, and violationCount recorded. Telemetry ingest writes one telemetry.batch_ingested entry per batch.