Station 1 of 6 · about 17 minutes leftRecord

Guide · EACH 1 · about 17 minutes

Your first governed agent, by API

One key, six calls, and a record at the end that an auditor would accept. You will register an agent, draw its lines, watch it refused outside them, and stop it. Nothing here needs your identity provider; that comes later, if you want it.

You need an EACH 1 account with a plan started (the Team trial counts) and a confirmed work email. Two steps are for a person in the dashboard, and each says so.

00 · 2 min

Get a key

In the dashboard, open People, then the API keys tab, and create a key. It is shown once. Keep it in your shell, not in a file you commit.

Shell
bash
export ENIYAN_API_KEY=et_...

Paste it once and every snippet on this page fills in. It stays in this tab's memory: it is never stored, never logged, and this page sends it nowhere.

Check that it works. This read answers with your account's default mode:

Check the key
bash
curl "https://api.eniyantrust.com/v1/verifications/scope-policy" \
  -H "Authorization: Bearer $ENIYAN_API_KEY"

What you should see

HTTP 200
json
{
  "default_scope_enforcement_mode": "advisory",
  "model_reverification_enforced": false
}

Advisory is the default: a call outside an agent's scopes is flagged, not refused. Station 02 changes that for your agent.

01 · 4 min

Register your first agent

One call names the agent, says what it is for, declares what it may touch (crm:read and tickets:write) and ties it to the person who answers for it.

operator_credential_id is your own operator credential. A colleague approves you on the Operators tab of People (an account's first two members approve each other). That is the whole human step, and no identity provider is involved. Once approved, your dashboard session reads the id as credential_id from GET /v1/me/operator-verification; put it in OPERATOR_CREDENTIAL_ID.

Register the agent
bash
curl -X POST "https://api.eniyantrust.com/v1/verifications/sessions" \
  -H "Authorization: Bearer $ENIYAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "ai_agent",
    "external_user_id": "support-agent",
    "required_level": "basic",
    "agent": {
      "purpose": "Tier 1 support: read tickets and draft replies for a person to send.",
      "autonomy_level": "human_on_loop",
      "access_mode": "read_write",
      "environment": "production",
      "data_classification": "confidential",
      "verification_product": "trust_layer",
      "operator_credential_id": "'"$OPERATOR_CREDENTIAL_ID"'",
      "delegated_scopes": [
        "crm:read",
        "tickets:write"
      ]
    }
  }'

What you should see

HTTP 201
json
{
  "id": "6e3ce212-25d3-43e7-8d6d-c675a83de8a7",
  "status": "created",
  "entity_type": "ai_agent",
  "external_user_id": "support-agent",
  "operator_credential_id": "f8ed470d-b48f-4e5d-bdb5-8bb968abeb7e",
  "delegated_scopes": [
    "crm:read",
    "tickets:write"
  ],
  "agent_identity_id": "0d62eab4-6286-4038-85aa-8cdeedcfaf94",
  "credential_id": null,
  "credential_token": null
}

The 9 fields that matter here, of the 32 in the answer. No credential yet. Put id in SESSION_ID.

An agent has no hosted identity check to pass, so you complete its registration yourself. The path says simulate because the same route stands in for the hosted check when a person's session is tested. For an agent it is the real completion in every environment.

Complete it
bash
curl -X POST "https://api.eniyantrust.com/v1/verifications/sessions/$SESSION_ID/simulate-success" \
  -H "Authorization: Bearer $ENIYAN_API_KEY"

What you should see

HTTP 200
json
{
  "id": "6e3ce212-25d3-43e7-8d6d-c675a83de8a7",
  "status": "verified",
  "completed_at": "2026-10-07T15:17:43.631932Z",
  "agent_identity_id": "0d62eab4-6286-4038-85aa-8cdeedcfaf94",
  "credential_id": "e564f54b-1116-4b01-b0aa-e68abbecadb7",
  "credential_token": "eyJhbGciOiJFUzI1NiIs\u2026"
}

The 6 fields that matter here, of the 32 in the answer. Put credential_id in CREDENTIAL_ID and credential_token in AGENT_TOKEN. The token is returned this once; it is cut short here.

If your account reviews agents enrolled with an API key, the completion call still returns the credential. The agent can do nothing until an admin admits it under Agents, on the Agent Fleet page.

02 · 3 min

Draw the lines

The lines are already declared: the two scopes in station 01. What is not decided yet is what happens outside them. Ask for crm:write now, under the default mode:

Ask for a scope outside the lines
bash
curl -X POST "https://api.eniyantrust.com/v1/credentials/verify" \
  -H "Authorization: Bearer $ENIYAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "credential_token": "'"$AGENT_TOKEN"'",
    "requested_scope": "crm:write"
  }'

What you should see

HTTP 200
json
{
  "valid": true,
  "reason": null,
  "scope_authorized": false,
  "scope_enforcement": "advisory"
}

The 4 fields that matter here, of the 25 in the answer. Flagged, and it still passes. Under advisory the violation is recorded and the call is valid.

Now switch this agent to block. This is a dashboard step for an org admin: open Agents, then Agent Fleet, then support-agent, and set Scope Enforcement to Block. The API refuses a key here on purpose: an agent holding the key must not be able to loosen its own lines. This is what a key gets:

What you should see

HTTP 403
json
{
  "detail": {
    "code": "ADMIN_REQUIRED",
    "message": "scope-enforcement policy writes require an org admin (dashboard) session"
  }
}

03 · 3 min

Make a call. See a refusal.

Run the same call again. This time it fails at the call, not in a report afterwards.

Outside the lines
bash
curl -X POST "https://api.eniyantrust.com/v1/credentials/verify" \
  -H "Authorization: Bearer $ENIYAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "credential_token": "'"$AGENT_TOKEN"'",
    "requested_scope": "crm:write"
  }'

What you should see

HTTP 200
json
{
  "valid": false,
  "reason": "scope_not_authorized",
  "scope_authorized": false,
  "scope_enforcement": "block"
}

The 4 fields that matter here, of the 25 in the answer.

Then ask for something inside them, crm:read:

Inside the lines
bash
curl -X POST "https://api.eniyantrust.com/v1/credentials/verify" \
  -H "Authorization: Bearer $ENIYAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "credential_token": "'"$AGENT_TOKEN"'",
    "requested_scope": "crm:read"
  }'

What you should see

HTTP 200
json
{
  "valid": true,
  "reason": null,
  "trust_level": "strong",
  "expires_at": "2031-10-06T15:17:43.654896Z",
  "scope_authorized": true,
  "scope_enforcement": "block"
}

The 6 fields that matter here, of the 25 in the answer. Verification is checked on the server every time, so the answer is always the current one.

04 · 3 min

Wire the webhook

Point the events at an endpoint you own (https, a public host) and the record arrives at your door as it happens. Change the URL to yours.

Register the endpoint
bash
curl -X POST "https://api.eniyantrust.com/v1/webhooks/subscriptions" \
  -H "Authorization: Bearer $ENIYAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/eniyan/webhooks",
    "events": [
      "credential.issued",
      "agent.scope_violation",
      "credential.revoked"
    ]
  }'

What you should see

HTTP 201
json
{
  "id": "e97fb8f6-93eb-4d0b-a170-65395e50c53c",
  "url": "https://example.com/eniyan/webhooks",
  "events": [
    "credential.issued",
    "credential.revoked",
    "agent.scope_violation"
  ],
  "enabled": true,
  "secret": "9b6929ed\u2026"
}

The 5 fields that matter here, of the 9 in the answer. The signing secret is returned this once; it is cut short here. Each delivery carries X-EniyanTrust-Signature, the HMAC-SHA256 of the raw body under that secret.

Run the refused call from station 03 once more. Your endpoint receives this request:

What you should see

Sent to your endpoint
json
X-EniyanTrust-Signature: sha256=b2652c62…
X-EniyanTrust-Event: agent.scope_violation
X-EniyanTrust-Delivery: fbfc74c6-10c0-4bf1-ad3f-490483b1c7b5

{
  "event": "agent.scope_violation",
  "id": "fbfc74c6-10c0-4bf1-ad3f-490483b1c7b5",
  "timestamp": "2026-10-07T15:17:44.368224+00:00",
  "org_id": "ddd610af-ab86-4997-9e06-1c6ee2145406",
  "data": {
    "agent_id": "34043ff4-43da-4fc8-9e69-3a39e40e7d20",
    "requested_scope": "crm:write",
    "enforcement": "block",
    "blocked": true,
    "delegated_scopes": [
      "crm:read",
      "tickets:write"
    ]
  }
}

credential.issued arrives the next time a credential is issued, and credential.revoked in the next station.

05 · 2 min

Stop it

Revoke the credential, then run the verify call again. It fails at once, signature or not.

Revoke the credential
bash
curl -X POST "https://api.eniyantrust.com/v1/credentials/$CREDENTIAL_ID/revoke" \
  -H "Authorization: Bearer $ENIYAN_API_KEY"

What you should see

HTTP 200
json
{
  "id": "e564f54b-1116-4b01-b0aa-e68abbecadb7",
  "external_user_id": "support-agent",
  "revoked": true,
  "revoked_at": "2026-10-07T15:17:44.392168Z"
}

The 4 fields that matter here, of the 36 in the answer.

Verify again
bash
curl -X POST "https://api.eniyantrust.com/v1/credentials/verify" \
  -H "Authorization: Bearer $ENIYAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "credential_token": "'"$AGENT_TOKEN"'",
    "requested_scope": "crm:read"
  }'

What you should see

HTTP 200
json
{
  "valid": false,
  "revoked": true,
  "reason": "Credential has been revoked"
}

The 3 fields that matter here, of the 25 in the answer.

And your endpoint receives:

What you should see

Sent to your endpoint
json
X-EniyanTrust-Signature: sha256=64c9dc76…
X-EniyanTrust-Event: credential.revoked
X-EniyanTrust-Delivery: d8b93423-f2c3-473b-99dd-353de4489de6

{
  "event": "credential.revoked",
  "id": "d8b93423-f2c3-473b-99dd-353de4489de6",
  "timestamp": "2026-10-07T15:17:44.396841+00:00",
  "org_id": "ddd610af-ab86-4997-9e06-1c6ee2145406",
  "data": {
    "credential_id": "e564f54b-1116-4b01-b0aa-e68abbecadb7",
    "external_user_id": "support-agent",
    "entity_type": "ai_agent",
    "revocation_reason": null,
    "revoked_at": "2026-10-07T15:17:44.392168+00:00"
  }
}

Revoking is permanent for this credential. To run the agent again, reissue its credential from its page in the dashboard; the declared scopes carry over.

To pause an agent without revoking anything, an org admin can stop it in the dashboard: Stop support-agent on the agent's page, or Stop in the bar at the top for every agent. A stopped agent is refused at its next check, which can be up to a minute later, and Release brings it back exactly as it was. Stopping is a dashboard action: the API refuses a key here too.

06 · optional

Later, when you want it

Everything above ran with one engineer and one key. When the fleet grows, the rest plugs in.