CydraLabs

Documentation

Quick start

Register an agent, give it a tool, send an action through CydraGateway, then add a policy that holds high-value payments for human approval. About 20 minutes.

Before you start

  • A CydraLabs account. Sign up for the free Developer plan (up to five non-production agents) or use your design-partner organisation.
  • curl and jq in a terminal. Every API call below goes to https://app.cydralabs.com/api/v1.
  • For step 7, a second person in your organisation: publishing a policy is a four-eyes operation.

1. Create your organisation

  1. Sign up with your work email and enter the six-digit code we email you.
  2. Sign in. Multi-factor authentication is mandatory: set up an authenticator app (or a passkey) and save your recovery codes.
  3. Create your organisation. You become its organisation administrator.

2. Add sample tools

The gateway authorises calls to tools in your inventory and denies unknown tools. Tools usually arrive through discovery from your model, identity and developer platforms. For this guide, use a mock adapter:

  1. Open Integrations and choose Add integration.
  2. Set Kind to anthropic, Mode to Mock, give it a name and save.
  3. Select Run discovery on its connector.

Discovery registers a sample MCP server (Treasury Payments MCP), sample agents and tools. This guide uses two of the tools:

Sample tools used in this guide
Tool keyPrivilege classWhat it does (simulated)
ledger.accounts.readreadReads general-ledger balances
payments.transfer.createfinancialCreates a supplier payment

3. Register an agent

Open Agents and choose Register agent. Give it a name (for example Quick start agent), a purpose, and leave the environment as development.

Production agents must also have an owner, a purpose and a data classification; the registry rejects them otherwise. On the Developer plan, use development or staging.

4. Grant permissions

CydraLabs is default-deny: an agent can call a tool when an explicit permission allows it (a binding). On the agent's Permissions tab, choose Grant permission twice:

  • ledger.accounts.read with the action invoke.
  • payments.transfer.create with the action invoke. You can add a constraint such as a maximum amount; calls above it are denied with PERMISSION_CONSTRAINT_EXCEEDED.

5. Issue a workload identity

Agents never use people's sessions. On the agent's Identities tab, choose Issue workload identity and copy the client ID and client secret. The secret is shown once. Store it in your secret manager, not in source code.

Exchange the credentials for a 5-minute agent token
API=https://app.cydralabs.com/api/v1
CLIENT_ID=cyd_...        # from the Identities tab
CLIENT_SECRET=...        # shown once

TOKEN=$(curl -s "$API/identities/token" \
  -H 'Content-Type: application/json' \
  -d "{\"grant_type\":\"client_credentials\",\"client_id\":\"$CLIENT_ID\",\"client_secret\":\"$CLIENT_SECRET\"}" \
  | jq -r .access_token)

The token is an Ed25519-signed JWT for the audience cydra-gateway, valid for five minutes. Request a new one when it expires; suspending the agent or revoking its sessions invalidates existing tokens.

6. Send an action

Agents submit each proposed tool call to POST /actions before anything runs. Every call needs an Idempotency-Key: resubmitting the same body with the same key returns the original result instead of running the action twice.

A read action
curl -s "$API/actions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: quickstart-read-$(date +%s)" \
  -H 'Content-Type: application/json' \
  -d '{
        "tool": "ledger.accounts.read",
        "operation": "invoke",
        "target": "ledger://accounts/operating",
        "parameters": {"account": "operating"},
        "justification": "Quick start: read a balance"
      }' | jq '{state: .action.status, decision: .decision.decision, reasons: .decision.reason_codes}'

Expected: state: "executed", decision: "allow" and the reason PERMISSION_GRANTED, from the baseline policy every organisation starts with. Try a tool you have not granted, or put something that looks like an API key in the parameters, and the gateway denies it with PERMISSION_NOT_GRANTED or SECRET_DETECTED.

7. Write a policy

Hold payments of £1,000 or more for a human. Open Policies, create a new policy and add one rule (the policy language page explains each part):

Rule
{
  "id": "QS-001",
  "description": "Payments of £1,000 or more need approval",
  "effect": "require_approval",
  "reason_code": "PAYMENT_ABOVE_THRESHOLD",
  "conditions": [
    {"field": "tool.privilege_class", "op": "eq",  "value": "financial"},
    {"field": "action.amount",        "op": "gte", "value": 1000}
  ],
  "obligations": {"approvals_required": 1}
}

Saving creates version 1 as a draft; drafts do not affect live decisions. Before publishing, use Test this draft on the policy page with your agent and payments.transfer.create: it runs the decision with the draft added to your active rules, and nothing is stored or executed.

Then ask a colleague with the publish policy permission to review and publish it. The person who wrote a version cannot publish it, so every policy change has two people behind it. To add a colleague, open Administration and invite them as an Organisation administrator or Security administrator, the roles that can publish policies.

8. Trigger and approve

A payment above the threshold
KEY="quickstart-payment-$(date +%s)"
cat > payment.json <<'EOF'
{
  "tool": "payments.transfer.create",
  "operation": "invoke",
  "target": "treasury://payments/supplier-run",
  "parameters": {"amount": 4800, "currency": "GBP", "beneficiary": "Harbor Logistics Ltd"},
  "justification": "Quick start: supplier payment"
}
EOF

curl -s "$API/actions" -H "Authorization: Bearer $TOKEN" -H "Idempotency-Key: $KEY" \
  -H 'Content-Type: application/json' -d @payment.json | tee result.json \
  | jq '{state: .action.status, decision: .decision.decision, reasons: .decision.reason_codes}'

Expected: state: "approval_required" with the reason PAYMENT_ABOVE_THRESHOLD. Nothing has run. Open Approvals, review the request and approve it with a reason. Then the agent fetches its approval token and resubmits the identical request with the same key:

Resubmit with the approval token
ACTION_ID=$(jq -r .action.id result.json)
APPROVAL_TOKEN=$(curl -s "$API/actions/gateway/$ACTION_ID" -H "Authorization: Bearer $TOKEN" \
  | jq -r .approval.approval_token)

jq --arg t "$APPROVAL_TOKEN" '. + {approval_token: $t}' payment.json > approved.json
curl -s "$API/actions" -H "Authorization: Bearer $TOKEN" -H "Idempotency-Key: $KEY" \
  -H 'Content-Type: application/json' -d @approved.json \
  | jq '{state: .action.status, output: .execution.output_redacted}'

Expected: state: "executed" and a simulated payment_reference beginning PAY-. The approval token is single-use, expires with the approval (15 minutes by default) and is bound to a hash of the exact request: changing the amount or beneficiary invalidates it. If the agent token expired while you were approving, request a new one before resubmitting.

9. Check the evidence

Open Evidence. Each decision (the allow, the approval request, the approval and the execution) is a signed record in your organisation's hash-linked evidence chain, and Actions shows the full timeline for each request. GET /evidence/verify recomputes the chain and its signatures and reports any break.

Next steps

  • Read the architecture to see what happens at each of the ten steps.
  • Learn rule fields, operators and precedence in the policy language.
  • Open the Security graph to see the effective authority of the agent you registered.

Troubleshooting

Common responses
ResponseMeaning
401 token_expiredThe 5-minute agent token expired. Exchange the credentials again.
deny · UNKNOWN_TOOLThe tool key is not in your inventory. Check the spelling or run discovery.
deny · PERMISSION_NOT_GRANTEDNo binding for this agent, tool and action. Grant it on the Permissions tab.
409 idempotency_conflictThe same Idempotency-Key was used with a different body. Use a new key.
403 four_eyes_requiredYou wrote this policy version; someone else must publish it.
429 rate_limitedToo many requests. Wait for the number of seconds in Retry-After.

Applies to the CydraLabs proof-of-concept platform. Last updated 4 October 2026. Questions or corrections: contact us.