Ironhide logoIRONHIDETest Your Agent

Agents

An agent registration is the durable record of a connected agent: its token and where its account stands on plan and usage. Ironhide runs one connection mode — ci: your real agent runs pre-production inside your own CI/CD, and your pipeline uploads what it did. Ironhide holds no transport config for your agent and never calls out to it.

Registration attaches the new agent to your account, so it is owner-authenticated: log in first and present your account owner key (wk_owner_...). Everything else on this page is authenticated with the agent's own token, or with your account owner key:

Authorization: Bearer wk_live_...

Register an agent#

POST /agents

Owner-authenticated: the new agent is attached to the account behind your owner key, and its billing (plan, subscription, the shared run pool) lives on that account.

Request body#

FieldTypeRequiredNotes
namestringyesMust be unique across your registrations (409 name_taken otherwise).
modestringyesMust be "ci". It is the only connection mode; any other value fails validation (422).
contact_emailstringnoOptional; used for report-ready email. Never shown to other tenants.
agent_versionstringnoYour own version label, default "v1". Runs are keyed by it.
contract_versionstringnoWire contract, default "v2". Explicit "v1" is retired for customers (403 contract_v1_retired).

Example#

curl -X POST https://ironhideai.com/agents \
  -H "Authorization: Bearer wk_owner_YOUR_OWNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "acme-sec-agent",
    "mode": "ci",
    "contact_email": "security@acme.dev"
  }'
{
  "agent_id": "agt_1a2b3c",
  "api_key": "wk_live_9f2c...",
  "data_rights": "owned",
  "status": "unverified",
  "contract_version": "v2",
  "plan_id": "developer",
  "subscription_status": "trialing",
  "free_run_limit": 100,
  "account_id": "acct_88de..."
}
Response fieldNotes
agent_idYour agent's id (agt_ plus hex). Used in every other endpoint.
api_keyThe wk_live_ token. Shown exactly once; only a SHA-256 hash is stored. This is your IRONHIDE_API_KEY.
data_rightsAlways owned.
statusAlways unverified at registration; call verify next.
contract_versionThe wire contract, v2 for new registrations.
plan_id, subscription_statusThe owning account's plan and status.
free_run_limitRuns included on the current tier (an alias of the tier's run quota).
account_idThe account this agent is attached to.

Errors: 403 operator_only, 403 contract_v1_retired, 409 name_taken, 422 validation_error, 503 contract_unavailable.

List agents#

GET /agents

Scoped to the caller — an agent's name and status are your business, not a public directory. An agent's token sees only that agent; your account owner key sees only that account's agents. Each row carries a recorded run count; no secret material ever leaves.

[
  {
    "agent_id": "agt_1a2b3c",
    "name": "acme-sec-agent",
    "mode": "ci",
    "data_rights": "owned",
    "status": "verified",
    "created_at": "2026-08-15T18:03:11.402910+00:00",
    "agent_version": "v1",
    "contract_version": "v2",
    "run_count": 12
  }
]

Update an agent#

PATCH /agents/{agent_id}

Owner-authenticated. Every field is optional; only what you send changes.

FieldNotes
name, contact_email, agent_versionPlain updates.
contract_versionChanging it resets status to unverified. Switching to v1 is operator-only.
rotate_api_keytrue mints a new wk_live_ token; the response carries api_key exactly once.
curl -X PATCH https://ironhideai.com/agents/agt_1a2b3c \
  -H "Authorization: Bearer wk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"rotate_api_key": true}'

Verify an agent#

POST /agents/{agent_id}/verify

Owner-authenticated. Verification marks the agent ready to drive episodes (409 agent_not_verified blocks the inbound routes until it is verified).

In the CI-inbound model there is no endpoint for Ironhide to probe — your own CI runs the agent, and the harness re-grades every submitted trajectory. So verification confirms the registration and stamps the agent verified; it does not reach out to anything.

curl -X POST https://ironhideai.com/agents/agt_1a2b3c/verify \
  -H "Authorization: Bearer wk_live_YOUR_KEY"
{
  "agent_id": "agt_1a2b3c",
  "passed": true,
  "status": "verified",
  "error_code": null,
  "message": "",
  "hint": "",
  "contract_version": "v2"
}

A streaming variant, POST /agents/{agent_id}/verify/stream, emits the same result as NDJSON for progress UIs.

Delete an agent#

DELETE /agents/{agent_id}

Owner-authenticated. Revokes the registration and its token. Runs already recorded keep the data rights they were stamped with.

{ "deleted": "agt_1a2b3c" }

Usage#

GET /agents/{agent_id}/usage

Owner-authenticated. The run meter for the agent's account:

{
  "agent_id": "agt_1a2b3c",
  "runs_used": 42,
  "run_quota": 100,
  "free_run_limit": 100,
  "runs_remaining": 58,
  "subscription_status": "trialing",
  "plan": { "id": "developer", "name": "Developer" }
}

free_run_limit is a historical alias of run_quota, kept for older clients. For an account-owned agent the meter is the account-level pool shared across its agents.

Plan#

GET /agents/{agent_id}/plan
POST /agents/{agent_id}/plan

Owner-authenticated. GET returns the current plan state:

{
  "agent_id": "agt_1a2b3c",
  "plan": { "id": "developer", "name": "Developer" },
  "subscription_status": "trialing",
  "effective_data_rights": "owned"
}

effective_data_rights is always "owned"; see data rights.

POST with {"plan_id": "..."} records a plan switch and returns the same shape. The plan catalog and prices are served by GET /plans; see the pricing page for the current tiers. Two refusals:

StatusCodeWhy
422unknown_planThe id is not in the catalog.
402requires_checkoutA price- or capability-increasing switch is routed through checkout, not applied for free on this route.