Rapid Response API¶
The NodeZero GraphQL API gives you programmatic access to Rapid Response data and actions. You can retrieve advisories, list affected assets, inspect event timelines, annotate assets with remediation status, and export host lists — all without touching the Portal UI.
Requirements
- API authentication is required. Store your JWT in
H3_API_JWT. - Rapid Response must be enabled on your account. Contact your Horizon3.ai representative if it is not available.
- For the full schema reference, see API Reference > Platform Features: Rapid Response.
All examples below use the H3_API_URL environment variable:
export H3_API_URL=https://api.gateway.horizon3ai.com/v1/graphql
export H3_API_URL=https://api.gateway.horizon3ai.eu/v1/graphql
List Rapid Response Advisories¶
rapid_responses_page returns a paginated list of advisories your organization has access to, including current status, vulnerability metadata, and per-advisory asset status breakdowns.
Full schema: Queries > rapid_responses_page and rapid_responses_count.
curl \
-X POST $H3_API_URL \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $H3_API_JWT" \
-d @- <<HERE
{
"query": "
query ListRapidResponses(\$page_input: PageInput) {
rapid_responses_page(page_input: \$page_input) {
rapid_responses {
uuid
name
short_name
status
test_available
vendors
products
asset_status_counts {
status
count
}
}
page_info {
page_num
page_size
}
}
}",
"variables": {
"page_input": {
"page_num": 1,
"page_size": 10
}
}
}
HERE
import os
import requests
query = '''
query ListRapidResponses($page_input: PageInput) {
rapid_responses_page(page_input: $page_input) {
rapid_responses {
uuid
name
short_name
status
test_available
vendors
products
asset_status_counts {
status
count
}
}
page_info {
page_num
page_size
}
}
}
'''
variables = {"page_input": {"page_num": 1, "page_size": 10}}
url = os.environ["H3_API_URL"]
headers = {"Authorization": f"Bearer {os.environ['H3_API_JWT']}"}
response = requests.post(url, headers=headers, json={"query": query, "variables": variables})
result = response.json() if response.status_code == 200 else None
{
"data": {
"rapid_responses_page": {
"rapid_responses": [
{
"uuid": "aaaa1111-bbbb-2222-cccc-dddd3333eeee",
"name": "CVE-2024-12345: Example Vendor Remote Code Execution",
"short_name": "CVE-2024-12345",
"status": "IN_PROGRESS",
"test_available": true,
"vendors": ["Example Vendor"],
"products": ["Example Product 1.x"],
"asset_status_counts": [
{ "status": "EXPLOITABLE", "count": 3 },
{ "status": "MITIGATED", "count": 1 }
]
}
],
"page_info": {
"page_num": 1,
"page_size": 10
}
}
}
}
List Affected Assets¶
rapid_response_assets_page returns one row per affected host per advisory. Filter by rapid_response_uuid to scope results to a specific advisory. Each row includes the current exposure status, when the window opened, and any linked ticket or annotation.
Exposure statuses: EXPLOITABLE, REGRESSED, POTENTIALLY_RELEVANT, VULNERABLE, MITIGATED, NOT_EXPLOITABLE
Full schema: Queries > rapid_response_assets_page.
curl \
-X POST $H3_API_URL \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $H3_API_JWT" \
-d @- <<HERE
{
"query": "
query ListRRAssets(\$page_input: PageInput) {
rapid_response_assets_page(page_input: \$page_input) {
rapid_response_assets {
uuid
asset_name
exposure_status
status_changed_at
is_external
exposure_opened_at
exposure_mitigated_at
last_annotation_status
last_ticket_uuid
}
page_info {
page_num
page_size
}
}
}",
"variables": {
"page_input": {
"page_num": 1,
"page_size": 25,
"filter_by_inputs": [
{
"field_name": "rapid_response_uuid",
"values": ["<rapid-response-uuid>"]
}
]
}
}
}
HERE
import os
import requests
query = '''
query ListRRAssets($page_input: PageInput) {
rapid_response_assets_page(page_input: $page_input) {
rapid_response_assets {
uuid
asset_name
exposure_status
status_changed_at
is_external
exposure_opened_at
exposure_mitigated_at
last_annotation_status
last_ticket_uuid
}
page_info {
page_num
page_size
}
}
}
'''
variables = {
"page_input": {
"page_num": 1,
"page_size": 25,
"filter_by_inputs": [
{"field_name": "rapid_response_uuid", "values": ["<rapid-response-uuid>"]}
]
}
}
url = os.environ["H3_API_URL"]
headers = {"Authorization": f"Bearer {os.environ['H3_API_JWT']}"}
response = requests.post(url, headers=headers, json={"query": query, "variables": variables})
result = response.json() if response.status_code == 200 else None
{
"data": {
"rapid_response_assets_page": {
"rapid_response_assets": [
{
"uuid": "bbbb2222-cccc-3333-dddd-eeee4444ffff",
"asset_name": "10.0.1.42",
"exposure_status": "EXPLOITABLE",
"status_changed_at": "2024-01-15T08:30:00Z",
"is_external": false,
"exposure_opened_at": "2024-01-15T08:30:00Z",
"exposure_mitigated_at": null,
"last_annotation_status": null,
"last_ticket_uuid": null
}
],
"page_info": {
"page_num": 1,
"page_size": 25
}
}
}
}
View the Advisory Timeline¶
rapid_response_events_page returns the RR-level timeline — one entry per lifecycle event (advisory published, pentest completed, ticket linked, annotation added, etc.).
Full schema: Queries > rapid_response_events_page.
curl \
-X POST $H3_API_URL \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $H3_API_JWT" \
-d @- <<HERE
{
"query": "
query RRTimeline(\$page_input: PageInput) {
rapid_response_events_page(page_input: \$page_input) {
rapid_response_events {
uuid
event_type
occurred_at
op_id
user_account_uuid
ticket_uuid
}
page_info {
page_num
}
}
}",
"variables": {
"page_input": {
"page_num": 1,
"page_size": 25,
"filter_by_inputs": [
{
"field_name": "rapid_response_uuid",
"values": ["<rapid-response-uuid>"]
}
],
"order_by": "occurred_at",
"sort_order": "DESC"
}
}
}
HERE
import os
import requests
query = '''
query RRTimeline($page_input: PageInput) {
rapid_response_events_page(page_input: $page_input) {
rapid_response_events {
uuid
event_type
occurred_at
op_id
user_account_uuid
ticket_uuid
}
page_info {
page_num
}
}
}
'''
variables = {
"page_input": {
"page_num": 1,
"page_size": 25,
"filter_by_inputs": [
{"field_name": "rapid_response_uuid", "values": ["<rapid-response-uuid>"]}
],
"order_by": "occurred_at",
"sort_order": "DESC"
}
}
url = os.environ["H3_API_URL"]
headers = {"Authorization": f"Bearer {os.environ['H3_API_JWT']}"}
response = requests.post(url, headers=headers, json={"query": query, "variables": variables})
result = response.json() if response.status_code == 200 else None
{
"data": {
"rapid_response_events_page": {
"rapid_response_events": [
{
"uuid": "cccc3333-dddd-4444-eeee-ffff5555aaaa",
"event_type": "NODEZERO_PENTEST",
"occurred_at": "2024-01-15T08:30:00Z",
"op_id": "abcdef12-3456-7890-abcd-ef1234567890",
"user_account_uuid": null,
"ticket_uuid": null
},
{
"uuid": "dddd4444-eeee-5555-ffff-aaaa6666bbbb",
"event_type": "VENDOR_ADVISORY",
"occurred_at": "2024-01-10T12:00:00Z",
"op_id": null,
"user_account_uuid": null,
"ticket_uuid": null
}
],
"page_info": {
"page_num": 1
}
}
}
}
View Per-Asset History¶
rapid_response_asset_events_page returns the history for a single asset — every pentest result, annotation, ticket link, and CSV download that affected it. Filter by rapid_response_asset_uuid to retrieve one asset's full timeline.
Full schema: Queries > rapid_response_asset_events_page.
curl \
-X POST $H3_API_URL \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $H3_API_JWT" \
-d @- <<HERE
{
"query": "
query AssetHistory(\$page_input: PageInput) {
rapid_response_asset_events_page(page_input: \$page_input) {
rapid_response_asset_events {
uuid
event_type
status
prior_status
}
page_info {
page_num
}
}
}",
"variables": {
"page_input": {
"page_num": 1,
"page_size": 25,
"filter_by_inputs": [
{
"field_name": "rapid_response_asset_uuid",
"values": ["<rapid-response-asset-uuid>"]
}
],
"order_by": "occurred_at",
"sort_order": "DESC"
}
}
}
HERE
import os
import requests
query = '''
query AssetHistory($page_input: PageInput) {
rapid_response_asset_events_page(page_input: $page_input) {
rapid_response_asset_events {
uuid
event_type
status
prior_status
}
page_info {
page_num
}
}
}
'''
variables = {
"page_input": {
"page_num": 1,
"page_size": 25,
"filter_by_inputs": [
{"field_name": "rapid_response_asset_uuid", "values": ["<rapid-response-asset-uuid>"]}
],
"order_by": "occurred_at",
"sort_order": "DESC"
}
}
url = os.environ["H3_API_URL"]
headers = {"Authorization": f"Bearer {os.environ['H3_API_JWT']}"}
response = requests.post(url, headers=headers, json={"query": query, "variables": variables})
result = response.json() if response.status_code == 200 else None
{
"data": {
"rapid_response_asset_events_page": {
"rapid_response_asset_events": [
{
"uuid": "eeee5555-ffff-6666-aaaa-bbbb7777cccc",
"event_type": "NODEZERO_PENTEST",
"status": "EXPLOITABLE",
"prior_status": null
}
],
"page_info": {
"page_num": 1
}
}
}
}
Add a Note or Mark Remediation Status¶
apply_annotations_to_rapid_response_assets lets you add a note or assign a remediation status to one or more assets. This is the primary "take action" mutation for Rapid Response — it records what your team did and why.
You must provide:
assets— a RapidResponseAssetsInput that selects which assets to annotate (by UUID, by status, or across the whole advisory)status— one of:TO_DO,FIXED,RISK_ACCEPTED,COMPENSATING_CONTROL,VERIFY_COMPENSATING_CONTROL,POTENTIAL_FALSE_POSITIVE,MUTE,CLOSEDtext— optional free-text note; required whenstatusisRISK_ACCEPTED
Each call creates a new annotation event visible in each affected asset's timeline.
Full schema: Mutations > apply_annotations_to_rapid_response_assets.
curl \
-X POST $H3_API_URL \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $H3_API_JWT" \
-d @- <<HERE
{
"query": "
mutation AnnotateRRAssets(\$input: ApplyAnnotationsToRapidResponseAssetsInput!) {
apply_annotations_to_rapid_response_assets(input: \$input) {
annotations_count
annotations {
uuid
status
text
}
}
}",
"variables": {
"input": {
"assets": {
"rapid_response_uuid": "<rapid-response-uuid>",
"rapid_response_asset_uuids": [
"<asset-uuid-1>",
"<asset-uuid-2>"
]
},
"status": "FIXED",
"text": "Patched in maintenance window 2024-01-20"
}
}
}
HERE
import os
import requests
query = '''
mutation AnnotateRRAssets($input: ApplyAnnotationsToRapidResponseAssetsInput!) {
apply_annotations_to_rapid_response_assets(input: $input) {
annotations_count
annotations {
uuid
status
text
}
}
}
'''
variables = {
"input": {
"assets": {
"rapid_response_uuid": "<rapid-response-uuid>",
"rapid_response_asset_uuids": [
"<asset-uuid-1>",
"<asset-uuid-2>"
]
},
"status": "FIXED",
"text": "Patched in maintenance window 2024-01-20"
}
}
url = os.environ["H3_API_URL"]
headers = {"Authorization": f"Bearer {os.environ['H3_API_JWT']}"}
response = requests.post(url, headers=headers, json={"query": query, "variables": variables})
result = response.json() if response.status_code == 200 else None
{
"data": {
"apply_annotations_to_rapid_response_assets": {
"annotations_count": 2,
"annotations": [
{
"uuid": "ffff6666-aaaa-7777-bbbb-cccc8888dddd",
"status": "FIXED",
"text": "Patched in maintenance window 2024-01-20"
},
{
"uuid": "89018901-8901-8901-8901-890189018901",
"status": "FIXED",
"text": "Patched in maintenance window 2024-01-20"
}
]
}
}
}
Annotate by status instead of by UUID
To annotate all EXPLOITABLE assets on an advisory at once, use rapid_response_asset_statuses instead of listing individual UUIDs:
"assets": {
"rapid_response_uuid": "<rapid-response-uuid>",
"rapid_response_asset_statuses": ["EXPLOITABLE"]
}
Download Affected Assets as CSV¶
rapid_response_asset_csv_presigned_url returns a presigned S3 URL for downloading a CSV of selected assets. The CSV is not returned inline — use the URL to fetch the file directly. Every download is recorded as an event on each affected asset's timeline.
Full schema: Queries > rapid_response_asset_csv_presigned_url.
curl \
-X POST $H3_API_URL \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $H3_API_JWT" \
-d @- <<HERE
{
"query": "
query RRAssetsCsv(\$rapid_response_assets: RapidResponseAssetsInput!) {
rapid_response_asset_csv_presigned_url(rapid_response_assets: \$rapid_response_assets)
}",
"variables": {
"rapid_response_assets": {
"rapid_response_uuid": "<rapid-response-uuid>",
"rapid_response_asset_statuses": ["EXPLOITABLE", "REGRESSED"]
}
}
}
HERE
import os
import requests
query = '''
query RRAssetsCsv($rapid_response_assets: RapidResponseAssetsInput!) {
rapid_response_asset_csv_presigned_url(
rapid_response_assets: $rapid_response_assets
)
}
'''
variables = {
"rapid_response_assets": {
"rapid_response_uuid": "<rapid-response-uuid>",
"rapid_response_asset_statuses": ["EXPLOITABLE", "REGRESSED"]
}
}
url = os.environ["H3_API_URL"]
headers = {"Authorization": f"Bearer {os.environ['H3_API_JWT']}"}
response = requests.post(url, headers=headers, json={"query": query, "variables": variables})
presigned_url = response.json()["data"]["rapid_response_asset_csv_presigned_url"]
# Fetch the CSV
csv_response = requests.get(presigned_url)
with open("rr_assets.csv", "wb") as f:
f.write(csv_response.content)
{
"data": {
"rapid_response_asset_csv_presigned_url": "https://s3.amazonaws.com/h3-exports/rapid-response-assets.csv?X-Amz-Signature=..."
}
}