Health
GET /health
no auth
Lightweight health check for load balancers. Does not query the database.
200 OK
Response
{
  "status": "healthy"
}
GET /api/health
no auth
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
no auth
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
no auth
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
ParameterTypeDescription
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/sessions/{id}
bearer
Get recall session metadata. Use node_ids to call /export.stix.
200 OK 404 not found
ParameterTypeDescription
id required pathUUIDRecall session ID
GET /api/recall/semantic
bearer
Pure embedding-based search. No keyword fallback.
ParameterTypeDescription
q requiredstringSearch query
limitintegerDefault 20, max 100
include_rejectedbooleanInclude rejected nodes
STIX Export (Authenticated)
GET /api/recall/sessions/{id}/export.stix
bearer
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
ParameterTypeDescription
id required pathstringRecall 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
bearer
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
bearer
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
bearer
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
bearer
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
bearer
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}
bearer
Get ingest batch metadata and all staged entities.
200 OK 404 not found
POST /api/ingest/batch/{id}/commit
bearer
Commit accepted staging records to kg_nodes. rejected records are discarded.
200 OK 404 not found
PATCH /api/ingest/staging/{id}
bearer
Accept, reject, merge, or mark uncertain a single staged entity.
FieldTypeDescription
status requiredenumpending | accepted | rejected | merged | uncertain
resolved_tlpstringOverride proposed TLP
merge_target_idUUIDRedirect entity to existing node (use with status=merged)
POST /api/ingest/report
bearer
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
admin/audit
CSV export of audit logs. Time range via from/to query params.
200 CSV download 403 audit:export required
ParameterTypeDescription
from querydate-timeStart time (ISO 8601)
to querydate-timeEnd time (ISO 8601)
limit queryintegerMax 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
admin/audit
Read audit log entries with optional filtering by user, time range, and pagination.
ParameterTypeDescription
user_id queryUUIDFilter by user
from querydate-timeStart time
to querydate-timeEnd time
limit queryintegerDefault 100
offset queryintegerDefault 0
Pilot (Public)
POST /api/pilot
no auth
Submit Design Partner Pilot application. Rate limited: 5 req/min per IP.
200 OK 400 validation error 429 rate limit
FieldTypeDescription
name requiredstringFull name (max 200 chars)
email requiredstringWork email
org requiredstringOrganization (max 200 chars)
role requiredenumcti-analyst | soc-manager | threat-intel-lead | security-engineer | ciso | other
cti_stackstringCurrent 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 →