Quick start with the SDK
Point the client at a gate, put the check where tools execute, and watch a payment stop before the signer runs.
Quick start with the SDK
By the end of this page your harness is asking the gate before it acts, the gate has refused something, and you have the receipt.
1. Check the gate answers
Health needs no key, because an uptime monitor holds no secrets.
curl -s https://gate.trustcoco.ai/demo/health{"status": "ok", "mode": "observe"}That endpoint is live, and health is the only route it answers without a key. The
steps from here run against a gate of your own, with the URL and API key you were
given on the hosted service, or against the container on your own machine at
http://localhost:8787. Standing one up takes one command and is in
Self-hosted, and getting a hosted gate starts at
trustcoco.ai.
2. Take the client
One file, no dependencies. sdk/python/coco_gate.py uses the Python standard
library, sdk/js/coco-gate.mjs uses the fetch built into Node 18. Vendor it into
your repository next to your agent code, or pin the package.
Your compliance team can read the whole file in one sitting, which is the review.
3. Put the check where tools execute
At the one place in your code that runs tool calls. Not in the agent's tool list.
from coco_gate import CocoGate
gate = CocoGate("https://gate.trustcoco.ai/demo", api_key=key, session_id=run_id)
verdict = gate.check("Bash", {"command": "rm -rf /"})
if verdict.allowed:
execute()
elif verdict.escalated:
park_for_a_person(verdict.reason)
else:
refuse(verdict.reason)An agent that consults a checker through its own tool list can also decline to consult it, and the verdict would land in the model's context, which is the one place a verdict can be argued with. The check goes where code and not judgement decides whether the call runs.
4. Watch it refuse
With your gate in enforce mode, a destructive command comes back like this and
your code does not run it. A new gate arrives in observe mode, so flip
COCO_MODE=enforce on your own container first, or ask for enforce on a hosted
trial gate.
curl -s -X POST "$GATE/check" \
-H "Authorization: Bearer $COCO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tool_name": "Bash", "tool_input": {"command": "rm -rf /"}, "session_id": "run-42"}'{"verdict": "BLOCK", "reason": "Coco: The command is a destructive operation with no safe reading in a governed workflow", "mode": "enforce"}In observe mode every answer is an ALLOW and the reason says so plainly. The verdict the contract actually reached is on the receipt, which is what makes an observe-mode ledger a truthful account of what enforce would have done.
{"verdict": "ALLOW", "reason": "observe mode, the verdict was recorded and nothing is enforced", "mode": "observe"}5. Gate a payment, with live state
A payment is where the SDK road earns itself, because the gate runs before the signer and the state it needs is in your ledger rather than on a disk.
verdict = gate.check(
"payment.send",
{
"amount": 10, "currency": "USD", "merchant": "acme",
"payee_address": "0xA11CE", "resource": "api-credits",
"offer_expires_epoch": 1787491200,
},
platform_state={
"monthly_spend_usd": 120,
"approved_payees": ["0xA11CE"],
"entitlements": [],
"historical_price_usd": 10,
"now_epoch": 1787490900,
},
)The snapshot comes from the platform that owns the ledger, never from the agent being judged. The gate records it on the receipt, so the verdict can be replayed against the exact state it read.
Against the payments contract set, with a 50 USD per-payment limit, a 200 USD monthly budget and a 1.25 variance ceiling.
| The call | Verdict |
|---|---|
| 10 USD to the registered payee, offer valid | ALLOW |
| 10 USD to an address one character off | BLOCK, the payee is not registered for this merchant |
| 20 USD when the resource has historically cost 10 | ESCALATE, over the allowed variance |
| 20 USD when 190 USD is already spent this month | BLOCK, it would cross the budget |
| An offer whose validity window has passed | BLOCK, an approval cannot resume a stale signature |
| No snapshot supplied at all | BLOCK, missing state fails closed |
The second row is the one people remember. An agent whose context has been poisoned accepts a lookalike address as readily as the real one, because to the model the two strings look equally plausible. The registry does not read plausibility.
Those contracts are a worked example rather than something you inherit. They live in the repository and a mandate has to name them. Yours will be your own.
6. Read the receipts
curl -s "$GATE/receipts?limit=50" -H "Authorization: Bearer $COCO_API_KEY"
curl -s "$GATE/ledger/verify" -H "Authorization: Bearer $COCO_API_KEY"{"ok": true, "rows": 12}If your gate has a dashboard, the same rows are there with the contract source scrolled to the failing line. See The dashboard.
What to know before you ship it
The client fails closed. Unreachable, unreadable and a refused key all return
BLOCK, because an unanswered check is not a yes. fail_open=True reverses it, and
it exists for the observe phase where the gate stops nothing anyway. Turn it off
when enforcement turns on.
check never raises on transport trouble, because a client that throws in the
payment path teaches people to wrap it in a bare except, and that unguards the call
silently.
Use one session_id per agent run. Without it the client generates a fresh id per
instance, every call looks like the first call of a new session, and no sequence
rule can ever fire.
With enforcement on and the client at its default, your agents stop until the gate answers. That is the honest cost of checking over a network, and you choose it rather than discover it.
Next
- Installation with the SDK for the full client and API reference
- Hosted for Coco running the gate for you
- State reference for every field a rule can read