Skip to main content

HTTP API

The HTTP API stores what HackAgent does: the agents you test, the attacks you configure, and every run with its results and traces. The dashboard reads it, and the SDK and CLI write to it in remote mode. It also offers an OpenAI-compatible LLM gateway that HackAgent uses for attacker and judge models.

Everything is JSON over HTTPS at https://api.hackagent.dev. The live OpenAPI schema is at /schema/?format=json, browsable as Swagger UI or ReDoc.

First request​

Create an API key in the dashboard, then list your agents:

curl -sS https://api.hackagent.dev/agent \
-H "Authorization: Bearer $HACKAGENT_API_KEY"
Example response
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "0b5f3c2e-6a1d-4f7e-9c8b-2d4e6f8a0b1c",
"name": "support-bot",
"endpoint": "http://localhost:8000/v1",
"agent_type": "OPENAI",
"description": "",
"metadata": {},
"organization": "7c9e2a41-3b5d-4e6f-8a7b-9c0d1e2f3a4b",
"organization_detail": {"id": "7c9e2a41-3b5d-4e6f-8a7b-9c0d1e2f3a4b", "name": "Acme"},
"created_at": "2026-09-29T10:12:03.418Z",
"updated_at": "2026-09-29T10:12:03.418Z"
}
]
}

Authentication​

Send the API key as a bearer token on every request: Authorization: Bearer <api-key>. The dashboard shows a new key once, when you create it; store it like a password. The SDK and CLI read it from HACKAGENT_API_KEY or from hackagent config set --api-key.

A missing, malformed or revoked key gets 401 with WWW-Authenticate: Bearer. A few account endpoints only work from a signed-in dashboard session; they answer an API key with 401 and "API key authentication not allowed on this endpoint". The resource sections below mark them dashboard only.

Conventions​

Paths have no trailing slash: /agent, not /agent/. Request bodies are JSON, and other content types get 415. Records are identified by UUID, except API keys (by their prefix) and traces and usage-log entries (by integer). You only ever see your own organization's records.

List endpoints are paginated with ?page=N, 10 items per page, and return {"count", "next", "previous", "results"}; next and previous are full URLs or null. Runs and results also take page_size, up to 100 and 1000.

Each user can make 1,000 requests per hour, shared by all of their keys. Past that, requests get 429 with a Retry-After header. A long SDK run makes a request for every result and every trace, so large campaigns can reach the limit; the SDK can also run locally instead.

How a run is recorded​

This is what HackAgent sends for each attack it runs in remote mode: from any campaign whose execution.storage.backend is remote. Any tool can record runs the same way.

The attack itself runs on your side; the API only stores it. POST /run/run_tests creates the run record, with is_client_executed: true, and returns it with 200.

Resources​

Agents​

An agent is a target you test. Its name is unique within your organization.

Method and pathDoes
GET /agentList agents
POST /agentCreate an agent (201)
GET /agent/{id}Get one
PUT / PATCH /agent/{id}Replace or update it
DELETE /agent/{id}Delete it with its attacks and runs (204)
Fields
FieldTypeNotes
namestring, requiredUp to 200 characters
endpointURL, requiredUp to 500 characters
agent_typestringHow HackAgent talks to it: OLLAMA, OPENAI, LITELLM, GOOGLE_ADK, LANGCHAIN, CLAUDE_CODE, CODEX, HERMES, WEB. Defaults to UNKNOWN. Agents registered by older clients may carry OPENAI_SDK
descriptionstring
metadataobjectFree-form
id, organization, organization_detail, created_at, updated_atread-onlySet by the server

Attacks​

An attack is a configured technique against one agent: type is the attack name (such as pair) and configuration the parameters and role models the campaign gave it.

Method and pathDoes
GET /attackList attacks
POST /attackCreate one: type, agent (UUID) and configuration are required (201)
GET / PUT / PATCH / DELETE /attack/{id}Get, replace, update or delete one

Responses add agent_name, owner_username and organization_name.

Runs​

A run is one execution of an attack.

Method and pathDoes
GET /runList runs. Filter with agent, attack, status or is_client_executed
POST /run/run_testsCreate a run for an agent and attack in your organization. Optional run_config is merged over the attack's configuration, and run_notes is free text. Returns the PENDING run with 200
GET /run/{id}Get one run with all of its results and their traces
PATCH /run/{id}Update status, run_notes or run_config
DELETE /run/{id}Delete it with its results and traces (204)

List items are summaries: instead of the results they carry attack_type, total_results, successful_jailbreaks, failed_jailbreaks, errors and not_evaluated counts. status is one of PENDING, RUNNING, COMPLETED, FAILED or CANCELLED.

Example: list the completed runs of one agent
curl -sS "https://api.hackagent.dev/run?agent=$AGENT_ID&status=COMPLETED&page_size=50" \
-H "Authorization: Bearer $HACKAGENT_API_KEY"

Results and traces​

A result is one attempt against the target: what was sent, what came back, and the verdict. Traces record the steps of that attempt in order.

Method and pathDoes
GET /resultList results. Filter with run or evaluation_status
POST /resultCreate a result; run (UUID) is required (201)
GET / PUT / PATCH / DELETE /result/{id}Get, replace, update or delete one. Responses include its traces
POST /result/{id}/traceAppend a trace step (201)
Result and trace fields
Result fieldTypeNotes
runUUID, required
request_payloadobjectWhat was sent to the target
response_status_code, response_headers, response_bodyThe target's reply
latency_msinteger
detected_tool_callsobject
evaluation_statusstringSee below
evaluation_notesstringThe judges' explanation
evaluation_metricsobjectScores
agent_specific_dataobjectTechnique-specific fields

evaluation_status is NOT_EVALUATED, SUCCESSFUL_JAILBREAK, FAILED_JAILBREAK, ERROR_AGENT_RESPONSE, ERROR_TEST_FRAMEWORK, PASSED_CRITERIA or FAILED_CRITERIA.

Trace fieldTypeNotes
sequenceinteger, requiredOrder within the result; unique per result
step_typestringTOOL_CALL, TOOL_RESPONSE, AGENT_THOUGHT, AGENT_RESPONSE_CHUNK, MCP_STEP, A2A_COMM or OTHER (the default)
contentobjectThe step itself
Example: count one run's successful attacks
curl -sS "https://api.hackagent.dev/result?run=$RUN_ID&evaluation_status=SUCCESSFUL_JAILBREAK" \
-H "Authorization: Bearer $HACKAGENT_API_KEY" | jq .count

Your account​

Method and pathDoes
GET /key/contextThe user and organization the key belongs to: user_id, username, organization_id, organization_name
GET /organization/meYour organization, including its credits balance
GET / PUT /user/meYour profile: email, first_name, last_name
GET / POST /key, GET / DELETE /key/{prefix}Manage API keys. Dashboard only: a key cannot create or delete keys
GET /apilogs, GET /apilogs/summaryGateway usage: tokens and credits per call, or per day between start_date and end_date (at most 92 days). Dashboard only

LLM gateway​

POST /v1/chat/completions is an OpenAI-compatible chat endpoint. HackAgent's default attacker, judge and category classifier use it in remote mode, and you can call it from any OpenAI client by setting the base URL to https://api.hackagent.dev/v1.

curl -sS https://api.hackagent.dev/v1/chat/completions \
-H "Authorization: Bearer $HACKAGENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Say hello"}], "max_tokens": 50}'

The server chooses the model and ignores model in the request. Streaming is not supported ("stream": true gets 501), and max_tokens is capped at 4,096. The response is a standard chat completion with usage token counts.

Errors​

Errors use the status code, with a JSON body in one of three shapes:

{"detail": "Authentication credentials were not provided."}

Validation errors (400) name each field instead: {"endpoint": ["Enter a valid URL."]}.

StatusMeans
400The body failed validation, or a gateway request had invalid JSON or max_tokens
401Missing or invalid key, or an API key on a dashboard-only endpoint
402Not enough credits for a gateway call (INSUFFICIENT_CREDITS)
403POST /run/run_tests with an agent or attack outside your organization, or a gateway call from an account without one
404No such record in your organization, or a page past the end
415The body is not JSON
429Rate limited; wait for Retry-After. The gateway also returns it, as PROVIDER_RATE_LIMITED, when the model provider is busy
501Streaming requested from the gateway
502, 504The gateway could not reach the model provider, or it timed out

The Python SDK raises ApiError for a failed API call; with raise_on_unexpected_status=True it raises UnexpectedStatus, carrying the status code and raw body, for undocumented statuses.