C2 Campaigns¶
C2 campaigns are the top-level organizational unit for red team engagements in Stentor. A campaign groups beacons, tasks, credentials, and listeners into a single operational context -- enabling per-engagement reporting, activity tracking, and credential aggregation across all associated sessions.
Overview¶
Every beacon can be assigned to exactly one campaign. Once associated, all tasks executed through that beacon and all credentials harvested from it are attributed to the campaign. This powers the reporting engine (PDF, DOCX, CSV, JSON, TSV, XML) and gives operators a unified view of engagement activity.
graph LR
C[Campaign] --> B1[Beacon 1]
C --> B2[Beacon 2]
C --> B3[Beacon 3]
B1 --> T1[Tasks]
B2 --> T2[Tasks]
B3 --> T3[Tasks]
B1 --> CR1[Credentials]
B2 --> CR2[Credentials]
C --> L[Linked Listeners]
L --> AB[Auto-associate<br/>new beacons]
C --> R[Reports & Export] Campaign Object¶
| Field | Type | Description |
|---|---|---|
id | UUID | Unique campaign identifier |
name | string | Campaign display name |
description | string | Free-text description |
status | string | active, paused, or archived |
created_by | UUID | Operator who created the campaign |
start_date | timestamp | Engagement start date (optional) |
end_date | timestamp | Engagement end date (optional) |
created_at | timestamp | Record creation time |
updated_at | timestamp | Last modification time |
recording_mode | boolean | Whether recording mode is enabled |
beacon_count | int | Live count of associated beacons |
credential_count | int | Live count of harvested credentials |
task_count | int | Live count of executed tasks |
Live Counts
The beacon_count, credential_count, and task_count fields are computed at query time and reflect the current state of the database. They are not stored on the campaign record itself.
Campaign CRUD¶
Create a Campaign¶
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Campaign name |
description | string | No | Campaign description |
start_date | RFC3339 | No | Engagement start date |
end_date | RFC3339 | No | Engagement end date |
curl -s -X POST "https://stentor.app/api/v1/c2/campaigns" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "ACME Corp Pentest Q1",
"description": "Quarterly external penetration test",
"start_date": "2026-02-01T00:00:00Z",
"end_date": "2026-02-28T23:59:59Z"
}' | jq
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "ACME Corp Pentest Q1",
"description": "Quarterly external penetration test",
"status": "active",
"created_by": "operator-uuid",
"start_date": "2026-02-01T00:00:00Z",
"end_date": "2026-02-28T23:59:59Z",
"created_at": "2026-02-21T10:00:00Z",
"updated_at": "2026-02-21T10:00:00Z",
"recording_mode": false,
"beacon_count": 0,
"credential_count": 0,
"task_count": 0
}
New campaigns are created with status: "active" by default.
List All Campaigns¶
Returns all campaigns for the authenticated operator, ordered by newest first. Each campaign in the response includes live beacon_count, credential_count, and task_count values computed via batch queries.
Get a Campaign¶
curl -s "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID" \
-H "Authorization: Bearer $TOKEN" | jq
Update a Campaign¶
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Updated name |
description | string | No | Updated description |
status | string | Yes | active, paused, or archived |
start_date | RFC3339 | No | Updated start date |
end_date | RFC3339 | No | Updated end date |
curl -s -X PUT "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "ACME Corp Pentest Q1 (Complete)",
"description": "Engagement completed successfully",
"status": "archived"
}' | jq
Valid Statuses
The status field must be one of active, paused, or archived. Any other value returns a 400 error.
Delete (Archive) a Campaign¶
Campaigns are soft-deleted by setting their status to archived. No data is permanently removed.
curl -s -X DELETE "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID" \
-H "Authorization: Bearer $TOKEN"
# Returns 204 No Content
Beacon Association¶
Beacons are assigned to campaigns either manually via the API or automatically through linked listeners. Each beacon can belong to at most one campaign at a time.
Associate a Beacon¶
| Field | Type | Required | Description |
|---|---|---|---|
beacon_id | UUID | Yes | ID of the beacon to associate |
curl -s -X POST "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID/beacons" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"beacon_id": "beacon-uuid-here"}' | jq
The association is persisted in both the database and the in-memory beacon registry.
Disassociate a Beacon¶
curl -s -X DELETE "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID/beacons/$BEACON_ID" \
-H "Authorization: Bearer $TOKEN"
# Returns 204 No Content
List Campaign Beacons¶
Returns all beacons currently associated with the campaign from the in-memory beacon registry.
curl -s "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID/beacons" \
-H "Authorization: Bearer $TOKEN" | jq
Listener Linking¶
Link listeners to a campaign so that any new beacon checking in through that listener is automatically assigned to the campaign. This eliminates the need to manually associate every new beacon.
Link a Listener¶
| Field | Type | Required | Description |
|---|---|---|---|
listener_id | UUID | Yes | ID of the listener to link |
curl -s -X POST "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID/listeners" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"listener_id": "listener-uuid-here"}' | jq
Idempotent Operation
Linking the same listener twice is safe -- the operation uses ON CONFLICT DO NOTHING and returns success without duplicating the association.
Unlink a Listener¶
curl -s -X DELETE "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID/listeners/$LISTENER_ID" \
-H "Authorization: Bearer $TOKEN"
# Returns 204 No Content
List Linked Listeners¶
Returns an array of listener UUID strings.
curl -s "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID/listeners" \
-H "Authorization: Bearer $TOKEN" | jq
Activity Log¶
The activity log provides a paginated, chronological view of all tasks executed across every beacon in the campaign.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | int | 50 | Maximum tasks per page (capped at 200) |
offset | int | 0 | Pagination offset |
Tasks are ordered newest-first. The total field enables pagination UI by reporting the full count of tasks across all campaign beacons.
Credential Aggregation¶
Retrieve all credentials harvested from any beacon in the campaign in a single query.
curl -s "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID/credentials" \
-H "Authorization: Bearer $TOKEN" | jq
Credential Object¶
| Field | Type | Description |
|---|---|---|
id | UUID | Credential record ID |
session_id | UUID | Associated session (optional) |
beacon_id | UUID | Beacon that harvested the credential |
credential_type | string | Type (e.g., ntlm, plaintext, kerberos) |
username | string | Account username |
domain | string | Domain name |
secret | string | Password hash or plaintext |
host | string | Source host |
source | string | How the credential was obtained (e.g., hashdump, mimikatz) |
note | string | Operator notes |
created_at | timestamp | When the credential was captured |
Recording Mode¶
Toggle recording mode on a campaign to flag it for enhanced activity logging.
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | Yes | Whether to enable recording mode |
curl -s -X POST "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID/record-mode" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": true}' | jq
Response: {"recording_mode": true}
Campaign Export¶
Export campaign data in multiple formats and report types through a single endpoint.
Formats¶
| Format | Content-Type | Notes |
|---|---|---|
pdf | application/pdf | All report types supported |
docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document | Supports additional options (see below) |
json | application/json | All report types supported |
csv | text/csv | All types except full |
tsv | text/tab-separated-values | All types except full |
xml | application/xml | All types except full |
Report Types¶
| Type | Key | Description |
|---|---|---|
| Full Campaign | full | Complete engagement summary combining all sections |
| Activity Timeline | activity | Chronological task execution with MITRE ATT&CK mappings |
| Hosts | hosts | Target systems with sessions and discovered services |
| Indicators of Compromise | indicators | Payloads, network IOCs, file/registry artifacts |
| Sessions | sessions | Beacon session details with command history |
| TTPs | ttp | MITRE ATT&CK technique usage with tactic statistics |
| Social Engineering | social_eng | Profiler visit analytics with geographic breakdowns |
Format Restrictions
The full report type is only available in pdf, json, and docx formats. Requesting full with csv, tsv, or xml returns a 400 error.
DOCX Options¶
The DOCX format supports additional query parameters for customization:
| Parameter | Type | Description |
|---|---|---|
title | string | Custom report title |
description | string | Description paragraph |
host_filter | string[] | Filter to specific hostnames |
mask_emails | bool | Replace emails with ***@***.*** |
mask_passwords | bool | Replace NTLM hashes with ******** |
Examples¶
For detailed information on report contents, see Campaign Reports.
Specialized Reports¶
Social Engineering Report¶
A dedicated endpoint for the social engineering report as JSON (separate from the export endpoint):
curl -s "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID/report/social-eng" \
-H "Authorization: Bearer $TOKEN" | jq
Custom Template Reports¶
Render campaign data through a custom Go template for bespoke reporting needs.
Execute template:
| Field | Type | Required | Description |
|---|---|---|---|
template | string | Yes | Go text/template string |
curl -s -X POST "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID/report/custom" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"template": "Campaign: {{.Name}} | Beacons: {{len .Beacons}}"}' | jq
Validate template (dry run):
curl -s -X POST "https://stentor.app/api/v1/c2/campaigns/$CAMPAIGN_ID/report/validate-template" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"template": "{{.Name}}"}' | jq
Response: {"valid": true, "error": ""} or {"valid": false, "error": "template: ...: unexpected ..."}.
API Reference¶
| Method | Endpoint | Description |
|---|---|---|
POST | /api/v1/c2/campaigns | Create a campaign |
GET | /api/v1/c2/campaigns | List all campaigns |
GET | /api/v1/c2/campaigns/:id | Get a campaign |
PUT | /api/v1/c2/campaigns/:id | Update a campaign |
DELETE | /api/v1/c2/campaigns/:id | Archive a campaign |
POST | /api/v1/c2/campaigns/:id/beacons | Associate a beacon |
DELETE | /api/v1/c2/campaigns/:id/beacons/:beaconId | Disassociate a beacon |
GET | /api/v1/c2/campaigns/:id/beacons | List campaign beacons |
POST | /api/v1/c2/campaigns/:id/listeners | Link a listener |
DELETE | /api/v1/c2/campaigns/:id/listeners/:listenerId | Unlink a listener |
GET | /api/v1/c2/campaigns/:id/listeners | List linked listeners |
GET | /api/v1/c2/campaigns/:id/activity | Get activity log |
GET | /api/v1/c2/campaigns/:id/credentials | Get aggregated credentials |
GET | /api/v1/c2/campaigns/:id/export | Export campaign report |
POST | /api/v1/c2/campaigns/:id/record-mode | Toggle recording mode |
GET | /api/v1/c2/campaigns/:id/report/social-eng | Social engineering report |
POST | /api/v1/c2/campaigns/:id/report/custom | Execute custom template |
POST | /api/v1/c2/campaigns/:id/report/validate-template | Validate custom template |
See Also¶
- Campaign Reports -- Report types, formats, and content details
- MITRE ATT&CK Integration -- Technique tracking and Navigator export
- Artifacts -- Auto-recorded IOCs from task execution
- Audit Log -- Operator action audit trail