API Reference
OpenAPI 3.1 reference for ThreatRecall. Browse endpoints below or
download the full spec as /openapi.json.
Auth endpoints require a Bearer JWT token (24h expiry).
Health
GET
/health
Lightweight health check for load balancers. Does not query the database.
200 OK
Response
{
"status": "healthy"
}
GET
/api/health
API service identity and version.
200 OK
Response
{
"status": "ok",
"service": "threatrecall",
"version": "1.0.0"
}
Recall (Public — No Auth)
ⓘ
These endpoints return only TLP:WHITE and TLP:GREEN nodes. Rate limited: 10 req/hour, 200 req/day per IP.
POST
/api/public/demo-recall
Keyword search against the live demo workspace (APT29, FIN7, Lazarus). PII is rejected before search.
200 OK
400 PII / missing query
429 Rate limit
| Parameter | Type | Description |
|---|---|---|
| query required | string | Natural language or keyword query. 1–300 chars. PII patterns rejected. |
Request body
{
"query": "APT29 Spear Phone"
}
Response
{
"results": [
{
"id": "uuid",
"node_type": "actor",
"name": "APT29",
"description": "...",
"source": "stix:...",
"tlp": "TLP:GREEN",
"confidence": 0.85,
"properties": {},
"linked_evidence_ids": ["ev-uuid"]
}
],
"seeded": true,
"query": "APT29 Spear Phone",
"result_count": 1,
"latency_ms": 42
}
Try it — live demo recall
STIX Export (Public — No Auth)
GET
/api/public/demo-export.stix
Search demo workspace and return a STIX 2.1 bundle. Content-Type: application/stix+json;version=2.1.
ⓘ
Only TLP:WHITE and TLP:GREEN nodes included. Bundle validated against STIX 2.1 meta-schema (fail-closed: HTTP 422 on failure).
200 STIX 2.1 bundle
400 missing q
404 no results
422 validation failed
429 rate limit
| Parameter | Type | Description |
|---|---|---|
| q required query | string | Search query, 1–300 chars |
| limit query | integer | Max results. Default 20, max 100. |
Try it — live demo STIX export
Recall (Authenticated)
ⓘ
All recall endpoints require
Authorization: Bearer <token>. Permission: recall:read. Results filtered to workspace TLP ceiling.
GET
/api/recall/search
Blended (keyword+embedding) or structured recall search. Returns results with a recall_id for STIX export.
200 OK
400 missing q
401 unauthenticated
403 missing permission
| Parameter | Type | Description |
|---|---|---|
| q required query | string | Search query |
| mode query | enum | blended (default) or structured |
| limit query | integer | Max results. Default 20, max 100. |
| include_rejected query | boolean | Include rejected nodes (admin/analyst only) |
Response
{
"results": [...],
"count": 5,
"query": "APT29 spear phone",
"mode": "blended",
"include_rejected": false,
"recall_id": "uuid",
"max_tlp": "TLP:GREEN"
}
GET
/api/recall/sessions/{id}
Get recall session metadata. Use node_ids to call /export.stix.
200 OK
404 not found
| Parameter | Type | Description |
|---|---|---|
| id required path | UUID | Recall session ID |
GET
/api/recall/semantic
Pure embedding-based search. No keyword fallback.
| Parameter | Type | Description |
|---|---|---|
| q required | string | Search query |
| limit | integer | Default 20, max 100 |
| include_rejected | boolean | Include rejected nodes |
STIX Export (Authenticated)
GET
/api/recall/sessions/{id}/export.stix
STIX 2.1 bundle for a recall session. Content-Type: application/stix+json;version=2.1.
⚠
TLP:RED enforcement: If session has TLP:RED nodes, workspace must have
red_export_grant=true. Without it, returns HTTP 403.
200 STIX 2.1 bundle
403 RED export denied
404 not found
422 validation failed
| Parameter | Type | Description |
|---|---|---|
| id required path | string | Recall session ID |
STIX 2.1 bundle shape
{
"type": "bundle",
"id": "bundle--...",
"spec_version": "2.1",
"objects": [
{
"type": "malware",
"spec_version": "2.1",
"id": "malware--...",
"created": "2024-01-15T00:00:00.000Z",
"modified": "2024-01-15T00:00:00.000Z",
"name": "CozyCar",
"confidence": 85,
"object_marking_refs": ["marking-definition--..."],
"external_references": [...]
}
]
}
Ingest
ⓘ
Requires
recall:write permission and Pro tier (knowledge_graph feature). TLP extracted from object labels or x_mitre_tlp custom property.
POST
/api/ingest/stix
Import STIX 2.1 bundle. Deduplicates by stix_id — safe to re-submit. Supported: indicator, malware, threat-actor, campaign, vulnerability, attack-pattern, course-of-action, intrusion-set.
201 created
400 missing bundle
403 feature unavailable
Request body
{
"bundle": {
"id": "bundle--4bd9e3a0-1234-5678-9abc-def012345678",
"type": "bundle",
"objects": [
{
"type": "malware",
"id": "malware--a3c2b1d0-...",
"name": "CozyCar",
"description": "APT29 custom malware",
"x_mitre_tlp": "GREEN",
"confidence": 85,
"created": "2024-01-15T00:00:00Z",
"external_references": [{"source_name": "ThreatRecall"}]
}
]
},
"default_tlp": "TLP:GREEN",
"default_confidence": 0.7
}
Response
{
"success": true,
"results": {
"nodes_created": 1,
"evidence_created": 1,
"skipped": 7
}
}
POST
/api/ingest/osint
Bulk-import OSINT collector feed. Node type inferred from keywords (CVE/ransomware → vulnerability, APT → actor, MITRE → attck).
201 created
Request body
{
"collector": "alienvault-otx",
"items": [
{
"title": "APT29 DNS tunneling indicator",
"description": "Malicious DNS resolution",
"url": "https://otx.alienvault.com/pulse/abc123",
"type": "ioc",
"tlp": "TLP:GREEN",
"tags": ["apt29", "dns"]
}
],
"default_tlp": "TLP:AMBER",
"default_confidence": 0.5
}
POST
/api/ingest/misp
Parse MISP event JSON. MISP distribution 0 → TLP:RED, 1 → TLP:AMBER, others use default_tlp.
Request body
{
"event": {
"uuid": "abc123",
"date": "2024-03-15",
"Attribute": [
{
"uuid": "def456",
"type": "md5",
"value": "d41d8cd98f00b204e9800998ecf8427e",
"to_ids": true,
"category": "Payload delivery"
}
]
},
"default_tlp": "TLP:AMBER",
"default_confidence": 0.6
}
POST
/api/ingest/analyst
Manual CTI entry from analyst notes.
Request body
{
"nodes": [
{
"name": "OPNUM21",
"node_type": "actor",
"description": "Probing infrastructure observed Jan 2025",
"tlp": "TLP:AMBER",
"confidence": 0.75
}
],
"default_tlp": "TLP:GREEN",
"default_confidence": 0.8
}
POST
/api/ingest/preview
LLM-assisted entity extraction from raw text. Entities staged for review; set ingest_mode=auto to commit immediately.
Request body
{
"text": "APT29 used Cobalt Strike beacons with C2 at 198.51.100.42 delivered via macro-enabled Word documents.",
"ingest_mode": "review"
}
Response
{
"success": true,
"batch_id": "batch-uuid",
"summary": {
"total_parsed": 3,
"total_to_add": 2,
"total_to_update": 0,
"duplicates": 0,
"missing_tlp": 1,
"sensitive_warns": 0
},
"entities": [
{
"id": "staged-uuid",
"status": "pending",
"node_type": "actor",
"name": "APT29",
"proposed_tlp": "TLP:AMBER",
"duplicate_candidates": [],
"warnings": [],
"extraction_reasoning": "..."
}
],
"auto_ingest": false
}
GET
/api/ingest/batch/{id}
Get ingest batch metadata and all staged entities.
200 OK
404 not found
POST
/api/ingest/batch/{id}/commit
Commit accepted staging records to kg_nodes. rejected records are discarded.
200 OK
404 not found
PATCH
/api/ingest/staging/{id}
Accept, reject, merge, or mark uncertain a single staged entity.
| Field | Type | Description |
|---|---|---|
| status required | enum | pending | accepted | rejected | merged | uncertain |
| resolved_tlp | string | Override proposed TLP |
| merge_target_id | UUID | Redirect entity to existing node (use with status=merged) |
POST
/api/ingest/report
Ingest structured report (parsed offline). Each content_block becomes an entity node.
Request body
{
"title": "APT29 Campaign Report Q1 2025",
"source_name": "internal-research",
"content_blocks": [
{
"entity_name": "APT29",
"node_type": "actor",
"description": "Russian state APT group",
"source_url": "https://internal/reports/apt29-q1",
"excerpt": "APT29 conducted spear phishing..."
}
],
"default_tlp": "TLP:GREEN",
"default_confidence": 0.6
}
Audit
ⓘ
Audit logs are write-once — UPDATE and DELETE are blocked by DB trigger. Export requires
audit:export (admin/audit role). Read requires audit:read.
GET
/api/audit/export
CSV export of audit logs. Time range via from/to query params.
200 CSV download
403 audit:export required
| Parameter | Type | Description |
|---|---|---|
| from query | date-time | Start time (ISO 8601) |
| to query | date-time | End time (ISO 8601) |
| limit query | integer | Max records. Default 1000, max 5000. |
CSV shape
id,tenant_id,user_id,user_email,action,resource,resource_id,outcome,ip_address,metadata,created_at
"abc123","tenant-uuid","user-uuid","analyst@contoso.com","recall.search","recall","","success","192.168.1.1","{}","2024-01-15T12:00:00Z"
GET
/api/audit/logs
Read audit log entries with optional filtering by user, time range, and pagination.
| Parameter | Type | Description |
|---|---|---|
| user_id query | UUID | Filter by user |
| from query | date-time | Start time |
| to query | date-time | End time |
| limit query | integer | Default 100 |
| offset query | integer | Default 0 |
Pilot (Public)
POST
/api/pilot
Submit Design Partner Pilot application. Rate limited: 5 req/min per IP.
200 OK
400 validation error
429 rate limit
| Field | Type | Description |
|---|---|---|
| name required | string | Full name (max 200 chars) |
| email required | string | Work email |
| org required | string | Organization (max 200 chars) |
| role required | enum | cti-analyst | soc-manager | threat-intel-lead | security-engineer | ciso | other |
| cti_stack | string | Current CTI stack (max 500 chars) |
Request body
{
"name": "Alex Chen",
"email": "alex.chen@contoso.com",
"org": "Contoso Security",
"role": "threat-intel-lead",
"cti_stack": "OpenCTI, Splunk, MISP"
}
Response
{ "success": true }
Looking for the integration compatibility matrix?
View Integrations Matrix →