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 /agentsOwner-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#
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Must be unique across your registrations (409 name_taken otherwise). |
mode | string | yes | Must be "ci". It is the only connection mode; any other value fails validation (422). |
contact_email | string | no | Optional; used for report-ready email. Never shown to other tenants. |
agent_version | string | no | Your own version label, default "v1". Runs are keyed by it. |
contract_version | string | no | Wire 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 field | Notes |
|---|---|
agent_id | Your agent's id (agt_ plus hex). Used in every other endpoint. |
api_key | The wk_live_ token. Shown exactly once; only a SHA-256 hash is stored. This is your IRONHIDE_API_KEY. |
data_rights | Always owned. |
status | Always unverified at registration; call verify next. |
contract_version | The wire contract, v2 for new registrations. |
plan_id, subscription_status | The owning account's plan and status. |
free_run_limit | Runs included on the current tier (an alias of the tier's run quota). |
account_id | The 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 /agentsScoped 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.
| Field | Notes |
|---|---|
name, contact_email, agent_version | Plain updates. |
contract_version | Changing it resets status to unverified. Switching to v1 is operator-only. |
rotate_api_key | true 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}/verifyOwner-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}/usageOwner-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}/planOwner-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:
| Status | Code | Why |
|---|---|---|
| 422 | unknown_plan | The id is not in the catalog. |
| 402 | requires_checkout | A price- or capability-increasing switch is routed through checkout, not applied for free on this route. |
IRONHIDE