Reference
How to call API v1
Base path is /api/v1. Replace YOUR_TENANT_HOST with your tenant hostname. Every mutation is audit-logged. Submissions still require analytical context — bare indicators are rejected.
Authentication
- Sign in to your tenant and open My account → Personal API tokens.
- Create a token. Copy the secret once — it is shown only at creation.
- Send it on every request:
Authorization: Bearer pk_live_…
Tokens inherit the caller’s role permissions (User, Admin, or Developer). Service principal secrets use the same header shape when configured by an admin.
import requests
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}Conventions
- Pagination — list routes accept
limit(1–100, default 50) and an optional page token in thecursorquery parameter. Responses wrap rows in{ data, pagination }. Passpagination.nextCursoras the next page token. - Soft delete — deleting an indicator deprecates it; deleting a case moves it to
cold. Evidence and audit are append-only (no PATCH/DELETE). - No bare indicators —
POST /api/v1/indicatorsrequirestype,value,activityClass,sensitivity, and non-emptycontext.
Licensing & attribution
Verdicts returned by the API are licensed for use inside your own product or workflow (an OEM embed — you surface our verdicts in your app). Two obligations apply and vary by plan — the verdict payload carries attributionRequired and attributionText so you can enforce them programmatically.
- Attribution required — wherever you surface a verdict to an end user, display the accompanying
attributionText(default “Verdict by Intellescope”) visibly next to it. Thepermalinkfield should remain reachable. Applies to Developer and Build embeds; waived on Volume and OEM. - No redistribution rights — you may embed verdicts in your product, but reselling, syndicating, or white-labeling them as a standalone data feed to third parties is not permitted below the OEM tier. The OEM tier — for resellers such as MSSPs (managed security service providers) that surface verdicts under their own brand — grants redistribution and white-label rights under a negotiated agreement.
| Plan | Attribution required | Redistribution rights |
|---|---|---|
| Developer | Required | Not permitted |
| Build | Required | Not permitted |
| Volume | Waived | Not permitted |
| OEM | Waived | Granted |
These terms summarize the licensing tiers; the binding terms are in your service agreement.
Errors
Failures return JSON with an error object. Validation failures may include a details array.
{
"error": {
"code": "unauthorized",
"message": "Invalid or missing API key"
}
}{
"error": {
"code": "forbidden",
"message": "Permission denied"
}
}{
"error": {
"code": "not_found",
"message": "Indicator not found"
}
}{
"error": {
"code": "bad_request",
"message": "Validation failed.",
"details": [
{
"code": "context_required",
"message": "Analytical context is required with every submission."
}
]
}
}Endpoint catalog
- GET
/api/v1/indicatorsList indicators in the tenant inventory (paginated).
Query: limit (1–100, default 50), cursor (page token)
Mock request & responseindicator.read - indicator.submit
- indicator.read
- PATCH
/api/v1/indicators/:idUpdate curation fields or transition state (published / deprecated).
Body: description?, tags?, activityClass?, state?: "published"|"deprecated"
Mock request & responseindicator.promote - DELETE
/api/v1/indicators/:idSoft-delete an indicator by deprecating it. Evidence is retained.
Mock request & responseindicator.deprecate - GET
/api/v1/evidenceList evidence claims (optionally filtered to one indicator).
Query: limit, cursor (page token), indicatorId. Evidence is append-only — no PATCH/DELETE.
Mock request & responseindicator.read - case.read
- case.create
- case.read
- case.assign
- DELETE
/api/v1/cases/:idSoft-delete a case by moving it to cold. History is retained.
Mock request & responsecase.dispose - GET
/api/v1/dispositionsList closed-loop disposition outcomes.
Query: limit, cursor (page token), indicatorId
Mock request & responsedisposition.read - GET
/api/v1/sourcesList contributing sources and reliability metadata.
Query: limit, cursor (page token)
Mock request & responseindicator.read - GET
/api/v1/threat-groupsList threat group records.
Query: limit, cursor (page token)
Mock request & responseindicator.read - indicator.read
- PATCH
/api/v1/threat-groups/:idUpdate threat group metadata (name, aliases, techniques).
Mock request & responseindicator.promote - indicator.read
- indicator.read
- indicator.promote
- indicator.deprecate
- GET
/api/v1/auditList audit events (own events unless admin).
Query: limit, cursor (page token). Audit is append-only — no PATCH/DELETE.
Mock request & responseaudit.read_own · audit.read_all - GET
/api/v1/users/meExport the authenticated user's personal data (GDPR Art. 15).
Mock request & responsesettings.read - POST
/api/v1/verdictsCompute an OEM verdict for one observable (unique-observable meter).
Headers: Idempotency-Key. Body: type, value, format?=json|stix. Sandbox: pk_test_.
Mock request & responseindicator.read - indicator.read
- indicator.read
- indicator.read
- indicator.read
- indicator.read
- indicator.read
- settings.read
Mock requests & responses
Examples use the Python requests library. IDs and timestamps are illustrative — use values from your tenant. Secrets are never returned by the API.
/api/v1/indicatorsList indicators
Returns a page of indicator summaries. Pass limit (1–100) and an optional page token from the previous page’s pagination.nextCursor.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/indicators?limit=2",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": [
{
"id": "ind_033",
"type": "ip",
"value": "203.0.113.45",
"state": "published",
"sensitivity": "TLP:AMBER",
"activityClass": "c2",
"tags": [
"beacon",
"finance"
],
"description": "Beaconing from finance subnet",
"createdAt": "2026-05-14T12:00:00.000Z",
"updatedAt": "2026-05-20T09:10:00.000Z",
"evidenceCount": 4
}
],
"pagination": {
"limit": 2,
"nextCursor": "ind_032",
"hasMore": true
}
}/api/v1/indicatorsSubmit an indicator
Creates a provisional indicator. Context is required — bare values are rejected. activityClass and sensitivity must use platform vocabulary.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
payload = {
"type": "ip",
"value": "203.0.113.50",
"activityClass": "c2",
"sensitivity": "TLP:AMBER",
"context": "Observed beaconing from finance subnet during IR-4412.",
"tags": [
"beacon"
]
}
response = requests.post(
f"{BASE}/api/v1/indicators",
headers=headers,
json=payload,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "ind_044",
"type": "ip",
"value": "203.0.113.50",
"state": "provisional",
"sensitivity": "TLP:AMBER",
"activityClass": "c2",
"tags": [
"beacon"
],
"createdAt": "2026-07-12T16:00:00.000Z",
"updatedAt": "2026-07-12T16:00:00.000Z",
"evidenceCount": 1
}
}/api/v1/indicators/:idGet indicator detail
Includes attached evidence claims and attribution arrays.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/indicators/ind_033",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "ind_033",
"type": "ip",
"value": "203.0.113.45",
"state": "published",
"sensitivity": "TLP:AMBER",
"activityClass": "c2",
"tags": [
"beacon"
],
"createdAt": "2026-05-14T12:00:00.000Z",
"updatedAt": "2026-05-20T09:10:00.000Z",
"evidenceCount": 2,
"threatGroupIds": [
"tg_apt29"
],
"campaignIds": [],
"mitreTechniques": [
"T1071"
],
"internalObservation": true,
"isMassScanner": false,
"evidence": [
{
"id": "ev_1",
"claimType": "reputation_assertion",
"claimSummary": "Malicious reputation from vendor feed",
"sourceId": "src_virustotal",
"sourceConfidence": 0.82,
"observedAt": "2026-05-19T00:00:00.000Z",
"assertedAt": "2026-05-19T00:05:00.000Z",
"signal": "supporting"
}
]
}
}/api/v1/indicators/:idUpdate or promote / deprecate
Patch curation fields, or set state to published (promote) or deprecated.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
payload = {
"description": "Confirmed C2 after IR-4412",
"state": "published"
}
response = requests.patch(
f"{BASE}/api/v1/indicators/ind_044",
headers=headers,
json=payload,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "ind_044",
"type": "ip",
"value": "203.0.113.50",
"state": "published",
"sensitivity": "TLP:AMBER",
"activityClass": "c2",
"tags": [
"beacon",
"confirmed"
],
"description": "Confirmed C2 after IR-4412",
"createdAt": "2026-07-12T16:00:00.000Z",
"updatedAt": "2026-07-12T16:05:00.000Z",
"evidenceCount": 1
}
}/api/v1/indicators/:idDeprecate an indicator
Soft-delete: sets state to deprecated. Evidence is retained for chain of custody.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.delete(
f"{BASE}/api/v1/indicators/ind_044",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "ind_044",
"type": "ip",
"value": "203.0.113.50",
"state": "deprecated",
"sensitivity": "TLP:AMBER",
"activityClass": "c2",
"tags": [
"beacon"
],
"createdAt": "2026-07-12T16:00:00.000Z",
"updatedAt": "2026-07-12T17:00:00.000Z",
"evidenceCount": 1
},
"meta": {
"softDeleted": true,
"state": "deprecated"
}
}/api/v1/evidenceList evidence
Evidence is append-only. Filter with indicatorId when you need claims for one IOC.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/evidence?indicatorId=ind_033&limit=10",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": [
{
"id": "ev_1",
"claimType": "human_analyst_assertion",
"claimSummary": "Observed beaconing from finance subnet during IR-4412.",
"sourceId": "src_analyst",
"sourceConfidence": 0.9,
"observedAt": "2026-07-12T15:55:00.000Z",
"assertedAt": "2026-07-12T16:00:00.000Z",
"signal": "supporting"
}
],
"pagination": {
"limit": 10,
"hasMore": false
}
}/api/v1/casesList cases
Analytical workspaces for coordinating response.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/cases?limit=5",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": [
{
"id": "case_a1",
"title": "Finance subnet beacon review",
"status": "open",
"severity": "elevated",
"indicatorIds": [
"ind_033"
],
"assigneeId": "u_priya",
"createdAt": "2026-07-10T10:00:00.000Z",
"updatedAt": "2026-07-11T08:00:00.000Z"
}
],
"pagination": {
"limit": 5,
"hasMore": false
}
}/api/v1/casesCreate a case
title, severity, and summary are required.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
payload = {
"title": "Finance subnet beacon review",
"severity": "elevated",
"summary": "Correlating C2 candidates from IR-4412.",
"indicatorIds": [
"ind_033"
]
}
response = requests.post(
f"{BASE}/api/v1/cases",
headers=headers,
json=payload,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "case_b2",
"title": "Finance subnet beacon review",
"status": "open",
"severity": "elevated",
"indicatorIds": [
"ind_033"
],
"assigneeId": "u_priya",
"createdAt": "2026-07-12T16:10:00.000Z",
"updatedAt": "2026-07-12T16:10:00.000Z"
}
}/api/v1/cases/:idGet one case
Fetch a single case by id.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/cases/case_b2",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "case_b2",
"title": "Finance subnet beacon review",
"status": "open",
"severity": "elevated",
"indicatorIds": [
"ind_033"
],
"assigneeId": "u_priya",
"createdAt": "2026-07-12T16:10:00.000Z",
"updatedAt": "2026-07-12T16:10:00.000Z"
}
}/api/v1/cases/:idUpdate a case
Change assignment, severity, summary, indicators, or status. Resolving statuses require resolutionNote.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
payload = {
"status": "in_progress",
"assignedTo": "u_alex"
}
response = requests.patch(
f"{BASE}/api/v1/cases/case_b2",
headers=headers,
json=payload,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "case_b2",
"title": "Finance subnet beacon review",
"status": "in_progress",
"severity": "elevated",
"indicatorIds": [
"ind_033"
],
"assigneeId": "u_alex",
"createdAt": "2026-07-12T16:10:00.000Z",
"updatedAt": "2026-07-12T16:20:00.000Z"
}
}/api/v1/cases/:idClose a case (cold)
Soft-delete: moves the case to cold. History remains.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.delete(
f"{BASE}/api/v1/cases/case_b2",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "case_b2",
"title": "Finance subnet beacon review",
"status": "cold",
"severity": "elevated",
"indicatorIds": [
"ind_033"
],
"assigneeId": "u_alex",
"createdAt": "2026-07-12T16:10:00.000Z",
"updatedAt": "2026-07-12T18:00:00.000Z"
},
"meta": {
"softDeleted": true,
"status": "cold"
}
}/api/v1/dispositionsList dispositions
Closed-loop outcomes when an indicator met your controls.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/dispositions?indicatorId=ind_033",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": [
{
"id": "disp_1",
"indicatorId": "ind_033",
"controlId": "ctrl_edr",
"controlKind": "edr",
"deployedAt": "2026-05-15T00:00:00.000Z",
"outcome": "blocked",
"analystConfirmed": true,
"encounterCount": 12,
"blockCount": 12,
"lastEncounterAt": "2026-05-20T08:00:00.000Z"
}
],
"pagination": {
"limit": 50,
"hasMore": false
}
}/api/v1/sourcesList sources
Contributing sources and reliability metadata for your tenant.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/sources",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": [
{
"id": "src_greynoise",
"name": "GreyNoise",
"category": "vendor",
"reliability": 0.94,
"independentDimension": "internet_scanning",
"description": "Internet-wide background noise classification."
}
],
"pagination": {
"limit": 50,
"hasMore": false
}
}/api/v1/threat-groupsList threat groups
Canonical actor records with aliases.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/threat-groups",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": [
{
"id": "tg_apt29",
"canonicalName": "APT29",
"aliases": [
"Cozy Bear",
"Midnight Blizzard"
],
"motivation": "espionage",
"origin": "RU",
"active": true,
"attackTechniques": [
"T1071",
"T1566"
]
}
],
"pagination": {
"limit": 50,
"hasMore": false
}
}/api/v1/threat-groups/:idGet one threat group
Fetch a single threat group by id.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/threat-groups/tg_apt29",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "tg_apt29",
"canonicalName": "APT29",
"aliases": [
"Cozy Bear",
"Midnight Blizzard"
],
"motivation": "espionage",
"origin": "RU",
"active": true,
"attackTechniques": [
"T1071",
"T1566"
]
}
}/api/v1/threat-groups/:idUpdate a threat group
Patch name, aliases, motivation, origin, active, or ATT&CK techniques.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
payload = {
"aliases": [
"Cozy Bear",
"Midnight Blizzard",
"The Dukes"
],
"justification": "Alias update from sector advisory"
}
response = requests.patch(
f"{BASE}/api/v1/threat-groups/tg_apt29",
headers=headers,
json=payload,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "tg_apt29",
"canonicalName": "APT29",
"aliases": [
"Cozy Bear",
"Midnight Blizzard",
"The Dukes"
],
"motivation": "espionage",
"origin": "RU",
"active": true,
"attackTechniques": [
"T1071",
"T1566"
]
}
}/api/v1/campaignsList campaigns
Campaign timeline records linked to threat groups.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/campaigns",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": [
{
"id": "camp_1",
"name": "Winter phishing wave",
"threatGroupId": "tg_apt29",
"startedAt": "2026-01-01T00:00:00.000Z",
"summary": "Credential harvest against finance users."
}
],
"pagination": {
"limit": 50,
"hasMore": false
}
}/api/v1/campaigns/:idGet one campaign
Fetch a single campaign by id.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/campaigns/camp_1",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "camp_1",
"name": "Winter phishing wave",
"threatGroupId": "tg_apt29",
"startedAt": "2026-01-01T00:00:00.000Z",
"summary": "Credential harvest against finance users."
}
}/api/v1/campaigns/:idUpdate a campaign
Update name, summary, dates, or threatGroupId.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
payload = {
"summary": "Credential harvest — closed after mailbox resets.",
"endedAt": "2026-02-01T00:00:00.000Z"
}
response = requests.patch(
f"{BASE}/api/v1/campaigns/camp_1",
headers=headers,
json=payload,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "camp_1",
"name": "Winter phishing wave",
"threatGroupId": "tg_apt29",
"startedAt": "2026-01-01T00:00:00.000Z",
"endedAt": "2026-02-01T00:00:00.000Z",
"summary": "Credential harvest — closed after mailbox resets."
}
}/api/v1/campaigns/:idDelete a campaign
Removes the campaign record. Prefer ending the campaign when history matters.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.delete(
f"{BASE}/api/v1/campaigns/camp_1",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "camp_1"
},
"meta": {
"deleted": true
}
}/api/v1/auditList audit events
Immutable activity log. Non-admins receive their own events; admins can read the full tenant log.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/audit?limit=5",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": [
{
"id": "aud_1",
"at": "2026-07-12T16:05:00.000Z",
"actorId": "u_priya",
"actorKind": "user",
"action": "indicator.promote",
"resourceKind": "indicator",
"resourceId": "ind_044"
}
],
"pagination": {
"limit": 5,
"hasMore": true,
"nextCursor": "aud_0"
}
}/api/v1/users/meExport my data
GDPR Art. 15 export of the authenticated user’s profile and API key metadata (secrets never included).
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/users/me",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"exportedAt": "2026-07-12T16:30:00.000Z",
"personalData": {
"id": "u_priya",
"email": "priya.iyer@example.com",
"fullName": "Priya Iyer",
"role": "user",
"positionTitle": "Senior Threat Intelligence Analyst"
},
"apiKeys": [
{
"id": "pak_1",
"name": "local triage script",
"prefix": "pk_live_…3d58",
"status": "active",
"createdAt": "2026-07-01T00:00:00.000Z"
}
]
}/api/v1/verdictsCompute a verdict
Computes an OEM verdict for one observable and bills a unique-observable if new this month. Send Idempotency-Key to make retries safe. Sandbox keys (pk_test_) return synthetic verdicts and never bill. Use format: "stix" for a STIX 2.1 Opinion note.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
payload = {
"type": "ip",
"value": "45.148.10.141"
}
response = requests.post(
f"{BASE}/api/v1/verdicts",
headers=headers,
json=payload,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "ver_9f2c17a4",
"observable": {
"type": "ip",
"value": "45.148.10.141"
},
"verdict": {
"score": 696,
"bucket": "high",
"confidence": 0.86,
"uncertaintyLow": 640,
"uncertaintyHigh": 742,
"completeness": 0.9
},
"hypotheses": [],
"evidence": [
{
"claimType": "reputation_assertion",
"claimSummary": "210k abuse reports — SSH brute-force, all blocked",
"signal": "supporting",
"sourceId": "src_abuseipdb",
"sourceName": "AbuseIPDB",
"observedAt": "2026-07-25T22:00:00.000Z",
"retrievedAt": "2026-07-26T12:00:00.000Z"
}
],
"engineVersion": "v0.3.1",
"hypothesisLibraryVersion": "hyp-lib-2026.07",
"computedAt": "2026-07-26T12:00:00.000Z",
"cacheAge": 0,
"permalink": "https://YOUR_TENANT_HOST/v/ver_9f2c17a4",
"attributionRequired": true,
"attributionText": "Verdict by Intellescope"
}
}/api/v1/verdicts/:idRetrieve a verdict
Fetches a previously computed verdict by id. Re-reads are free — they do not consume unique-observable quota.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/verdicts/ver_9f2c17a4",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "ver_9f2c17a4",
"observable": {
"type": "ip",
"value": "45.148.10.141"
},
"verdict": {
"score": 696,
"bucket": "high",
"confidence": 0.86,
"uncertaintyLow": 640,
"uncertaintyHigh": 742,
"completeness": 0.9
},
"hypotheses": [],
"evidence": [
{
"claimType": "reputation_assertion",
"claimSummary": "210k abuse reports — SSH brute-force, all blocked",
"signal": "supporting",
"sourceId": "src_abuseipdb",
"sourceName": "AbuseIPDB",
"observedAt": "2026-07-25T22:00:00.000Z",
"retrievedAt": "2026-07-26T12:00:00.000Z"
}
],
"engineVersion": "v0.3.1",
"hypothesisLibraryVersion": "hyp-lib-2026.07",
"computedAt": "2026-07-26T12:00:00.000Z",
"cacheAge": 0,
"permalink": "https://YOUR_TENANT_HOST/v/ver_9f2c17a4",
"attributionRequired": true,
"attributionText": "Verdict by Intellescope"
}
}/api/v1/verdicts/:id/outcomeReport an outcome
Report how the verdict resolved in your environment. Free and unmetered — this closed-loop signal improves calibration.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
payload = {
"outcome": "malicious",
"note": "Blocked at the mail gateway."
}
response = requests.post(
f"{BASE}/api/v1/verdicts/ver_9f2c17a4/outcome",
headers=headers,
json=payload,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"verdictId": "ver_9f2c17a4",
"outcome": "malicious",
"note": "Blocked at the mail gateway.",
"reportedAt": "2026-07-26T13:00:00.000Z",
"reporterTenantId": "tnt_acme"
},
"metered": false,
"message": "Outcome recorded. This endpoint is free and does not consume unique-observable quota."
}/api/v1/batchesSubmit a batch
Queues an async batch of observables and returns 202 with a poll URL. Each unique observable in the batch meters the same as a single verdict.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
payload = {
"items": [
{
"type": "ip",
"value": "45.148.10.141"
},
{
"type": "domain",
"value": "malware-c2.example"
}
]
}
response = requests.post(
f"{BASE}/api/v1/batches",
headers=headers,
json=payload,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "batch_7c3d10f2",
"status": "queued",
"createdAt": "2026-07-26T13:05:00.000Z",
"itemCount": 2,
"poll": "/api/v1/batches/batch_7c3d10f2",
"results": "/api/v1/batches/batch_7c3d10f2/results"
}
}/api/v1/batches/:idPoll batch status
Poll for job status. A worker that stalls for 15 minutes is reaped to failed so a job never hangs forever.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/batches/batch_7c3d10f2",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"id": "batch_7c3d10f2",
"status": "completed",
"createdAt": "2026-07-26T13:05:00.000Z",
"updatedAt": "2026-07-26T13:05:12.000Z",
"completedAt": "2026-07-26T13:05:12.000Z",
"itemCount": 2,
"resultCount": 2,
"error": null
}
}/api/v1/batches/:id/resultsRetrieve batch results
Returns the completed verdict products once status is completed.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/batches/batch_7c3d10f2/results",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": [
{
"id": "ver_9f2c17a4",
"observable": {
"type": "ip",
"value": "45.148.10.141"
},
"verdict": {
"score": 696,
"bucket": "high",
"confidence": 0.86,
"uncertaintyLow": 640,
"uncertaintyHigh": 742,
"completeness": 0.9
},
"hypotheses": [],
"evidence": [
{
"claimType": "reputation_assertion",
"claimSummary": "210k abuse reports — SSH brute-force, all blocked",
"signal": "supporting",
"sourceId": "src_abuseipdb",
"sourceName": "AbuseIPDB",
"observedAt": "2026-07-25T22:00:00.000Z",
"retrievedAt": "2026-07-26T12:00:00.000Z"
}
],
"engineVersion": "v0.3.1",
"hypothesisLibraryVersion": "hyp-lib-2026.07",
"computedAt": "2026-07-26T12:00:00.000Z",
"cacheAge": 0,
"permalink": "https://YOUR_TENANT_HOST/v/ver_9f2c17a4",
"attributionRequired": true,
"attributionText": "Verdict by Intellescope"
},
{
"id": "ver_4b8e02d1",
"observable": {
"type": "domain",
"value": "malware-c2.example"
},
"verdict": {
"score": 696,
"bucket": "high",
"confidence": 0.86,
"uncertaintyLow": 640,
"uncertaintyHigh": 742,
"completeness": 0.9
},
"hypotheses": [],
"evidence": [
{
"claimType": "reputation_assertion",
"claimSummary": "210k abuse reports — SSH brute-force, all blocked",
"signal": "supporting",
"sourceId": "src_abuseipdb",
"sourceName": "AbuseIPDB",
"observedAt": "2026-07-25T22:00:00.000Z",
"retrievedAt": "2026-07-26T12:00:00.000Z"
}
],
"engineVersion": "v0.3.1",
"hypothesisLibraryVersion": "hyp-lib-2026.07",
"computedAt": "2026-07-26T12:00:00.000Z",
"cacheAge": 0,
"permalink": "https://YOUR_TENANT_HOST/v/ver_4b8e02d1",
"attributionRequired": true,
"attributionText": "Verdict by Intellescope"
}
]
}/api/v1/usageCheck usage
Unique-observable usage for the current billing month. Soft-capped — you are never hard-blocked; overage bills per unit above the pool.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/usage",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"data": {
"plan": "build",
"periodStart": "2026-07-01T00:00:00.000Z",
"periodEnd": "2026-08-01T00:00:00.000Z",
"uniqueUsed": 6482,
"uniqueLimit": 10000,
"uniqueRemaining": 3518,
"overageUnits": 0,
"overageUsdPerUnit": 0.06,
"softCapped": true,
"alertAt80": false,
"alertAt100": false,
"features": {
"webhooks": true,
"sla": null,
"attributionRequired": true,
"redistributionRights": false
}
}
}/api/v1/openapiFetch the OpenAPI document
Published OpenAPI 3.0 spec for the whole v1 surface. Public — the Authorization header is optional here.
import requests
BASE = "https://YOUR_TENANT_HOST"
TOKEN = "pk_live_••••••••••••"
headers = {
"Authorization": f"Bearer {TOKEN}",
}
response = requests.get(
f"{BASE}/api/v1/openapi",
headers=headers,
timeout=30,
)
response.raise_for_status()
data = response.json()
print(data){
"openapi": "3.0.3",
"info": {
"title": "Intellescope API",
"version": "v1"
},
"paths": {
"/api/v1/verdicts": {
"post": {
"summary": "Compute an OEM verdict"
}
}
}
}