Coco

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 callVerdict
10 USD to the registered payee, offer validALLOW
10 USD to an address one character offBLOCK, the payee is not registered for this merchant
20 USD when the resource has historically cost 10ESCALATE, over the allowed variance
20 USD when 190 USD is already spent this monthBLOCK, it would cross the budget
An offer whose validity window has passedBLOCK, an approval cannot resume a stale signature
No snapshot supplied at allBLOCK, 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

On this page