# Prophet Docs Full LLM Context Prophet is autonomous network breach detection and attribution delivered as a service: collectors extract the shape of network communication — and the host context behind it — and Prophet turns it into finished investigations (who got in, how, as what identity, what they ran, and where the data went). Use this file as the plain-text, crawler-friendly companion to the interactive Prophet docs. Prefer canonical API contract details from openapi.json when building tools, and use this file for explanations, SDK examples, and endpoint notes. # Prophet API Reference Canonical docs: https://docs.prophet.io/ Marketing site: https://prophetic.ai/ Console: https://console.prophet.io/ API base URL: https://app.prophet.io OpenAPI: https://docs.prophet.io/openapi.json This Markdown export is generated from the same endpoint metadata used by the interactive docs app. ## Navigation - Overview: https://docs.prophet.io/#overview - Quickstart: https://docs.prophet.io/#quickstart - Authentication: https://docs.prophet.io/#authentication - Collector config: https://docs.prophet.io/#collector-config - Python SDK: https://docs.prophet.io/#sdk - PQL: https://docs.prophet.io/#pql - API reference: https://docs.prophet.io/#api - Quickstart / API key: https://docs.prophet.io/#quickstart-api-key - Quickstart / Token exchange: https://docs.prophet.io/#quickstart-token - Quickstart / Deploy node: https://docs.prophet.io/#quickstart-node - Quickstart / Registration: https://docs.prophet.io/#quickstart-registration ## API Families - OAuth2: Issue JWT access tokens, register collectors, and run device authorization. (https://docs.prophet.io/#api-oauth2) - Deployments: Create and manage child tenant deployments for MSP parent accounts. (https://docs.prophet.io/#api-deployments) - Nodes: Provision units, inspect node health, trigger updates, and pull diagnostics. (https://docs.prophet.io/#api-nodes) - Profiles: Reusable collector capture configuration for fleets and child deployments. (https://docs.prophet.io/#api-profiles) - Collector: Download binaries and install or uninstall the Prophet collector. (https://docs.prophet.io/#api-collector) - Search: Query flow records, request timeseries buckets, and run terms aggregations over flow data. (https://docs.prophet.io/#api-flows) - Investigations: Read the finished investigations Prophet produces when breach signal appears — verdict, key findings, provenance lineage across access, execution, and network, and recommended actions. One question is left for a human: was this authorized? (https://docs.prophet.io/#api-investigations) - Explore: External-organization communication shape: which external services a network sends traffic to, and the texture of each relationship (when, rhythm, transfer, who, how). Communication shape is what Prophet models to detect breaches — these endpoints expose the same view of your network for exploration. (https://docs.prophet.io/#api-explore) - Events: Fetch event topics and records from Prophet plugin activity. (https://docs.prophet.io/#api-events) - Automation: List, set, and delete automations scoped to a tenant or child tenant. (https://docs.prophet.io/#api-automation) ## OAuth2 Issue JWT access tokens, register collectors, and run device authorization. ### POST /rest/oauth2/token/1.0 — Exchange API credentials Docs URL: https://docs.prophet.io/#api-oauth-token Family: OAuth2 Auth: client credentials Scope: public Version: 1.0 Stability: stable Returns a JWT bearer token from a client ID and client secret. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | client_id | string | yes | OAuth2 API credential client ID. | | body | client_secret | string | yes | OAuth2 API credential secret. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | access_token | string | n/a | JWT bearer token. | | response | expires_in | number | n/a | Lifetime in seconds. | | response | expires_at | number | n/a | Unix timestamp when the token expires. | | response | token_type | string | n/a | Usually Bearer. | REST example: ```bash curl -X POST https://app.prophet.io/rest/oauth2/token/1.0 \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=YOUR_CLIENT_ID" \ -d "client_secret=YOUR_CLIENT_SECRET" ``` Example response: ```json { "access_token": "eyJhbGciOi...", "expires_in": 86400, "expires_at": 1770240000, "token_type": "Bearer" } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | invalid_credentials | Client credentials are missing or invalid. | ### POST /rest/oauth2/register/1.0 — Register legacy node Docs URL: https://docs.prophet.io/#api-oauth-register Family: OAuth2 Auth: bearer token Scope: p.token.scope.ingest Version: 1.0 Stability: legacy Registers a first-generation node by machine ID and hostname. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | machine_id | string | yes | Stable node machine identifier. | | body | hostname | string | yes | Host name to store on the legacy node record. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | node_id | string | n/a | Existing or newly-created legacy node ID. | Example response: ```json { "node_id": "4f8ce2a8-7f2e-4c77-a7fa-904f0a9d88b1" } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 400 | missing_required_fields | machine_id or hostname is missing. | ### POST /rest/oauth2/device/1.0 — Start device authorization Docs URL: https://docs.prophet.io/#api-oauth-device-start Family: OAuth2 Auth: none Scope: public Version: 1.0 Stability: stable Creates a pending device authorization request for a machine ID or node ID. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | machine_id | string | no | Machine identifier. Required unless node_id is supplied. | | body | node_id | string | no | Node identifier alias. Used as machine_id when supplied. | | body | hostname | string | yes | Host name shown during approval. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | device_code | string | n/a | Opaque code the device polls with. | | response | auth_url | string | n/a | Full per-device approval URL on the Prophet console. Present or open it for an operator. | | response | poll_interval | number | n/a | Seconds to wait between poll requests. | | response | expires_in | number | n/a | Seconds until this device authorization expires. | REST example: ```bash curl -X POST https://app.prophet.io/rest/oauth2/device/1.0 \ -H "Content-Type: application/json" \ -d '{"machine_id":"machine-123","hostname":"edge-01"}' ``` Example response: ```json { "device_code": "dev_abc123", "auth_url": "https://console.prophet.io/auth/device/dev_abc123", "poll_interval": 5, "expires_in": 900 } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 400 | error | machine_id/node_id or hostname is missing. Body is {"error":"machine_id and hostname are required"}. | ### GET /rest/oauth2/device/approve/1.0 — Get device approval info Docs URL: https://docs.prophet.io/#api-oauth-device-info Family: OAuth2 Auth: none Scope: public Version: 1.0 Stability: stable Reads pending device details for an approval screen. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | query | device_code | string | yes | Device code returned by the start request. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | device_code | string | n/a | Device authorization code. | | response | node_id | string \| null | n/a | Node ID when present. | | response | hostname | string | n/a | Device hostname. | | response | status | string | n/a | Pending, approved, denied, or expired status. | | response | createdAt | string | n/a | Creation timestamp. | | response | expiresAt | string | n/a | Expiration timestamp. | Example response: ```json { "device_code": "dev_abc123", "node_id": "node-123", "hostname": "edge-01", "status": "pending", "createdAt": "2026-02-04T16:00:00.000Z", "expiresAt": "2026-02-04T16:10:00.000Z" } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 400 | missing_required_fields | device_code is missing. | | 404 | not_found | Device code not found. | ### POST /rest/oauth2/device/approve/1.0 — Approve or deny device Docs URL: https://docs.prophet.io/#api-oauth-device-approve Family: OAuth2 Auth: bearer token Scope: any valid token Version: 1.0 Stability: stable Approves a pending device for the authenticated customer or denies the request. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | device_code | string | yes | Device code to approve or deny. | | body | action | "deny" \| omitted | no | When action is deny the code is denied; otherwise it is approved. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | "approved" \| "denied" | n/a | Result of the action. | Example response: ```json { "status": "approved" } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 400 | missing_required_fields | device_code is missing. | ### POST /rest/oauth2/device/poll/1.0 — Poll device authorization Docs URL: https://docs.prophet.io/#api-oauth-device-poll Family: OAuth2 Auth: none Scope: public Version: 1.0 Stability: stable Returns the current state of a device authorization request. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | device_code | string | yes | Device code returned by the start request. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | "pending" \| "approved" \| "denied" \| "expired" | n/a | Current device authorization status. Unknown device codes return status expired, not an error. | | response | access_key | string \| omitted | n/a | Present once approved. A long-lived client credential in client_id.client_secret form — exchange it at /rest/oauth2/token/1.0 for bearer tokens. It is not itself a bearer token. | | response | node_id | string \| omitted | n/a | Node identifier bound to the approval, when present. | Example response: ```json { "status": "approved", "access_key": "clientid.secretpart", "node_id": "node-123" } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 400 | error | device_code is missing. Body is {"error":"device_code is required"}. | ## Deployments Create and manage child tenant deployments for MSP parent accounts. ### GET /rest/deployments/1.0 — List child deployments Docs URL: https://docs.prophet.io/#api-deployments-list Family: Deployments Auth: bearer token Scope: p.token.scope.deployment_api Version: 1.0 Stability: stable Lists child tenants for the authenticated parent MSP. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | query | parent_id | string | no | Parent MSP customer_id. Defaults to the authenticated customer_id. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | string | n/a | Always "success" on 200. | | response | parent.customer_id | string | n/a | The parent deployment customer_id used for the query. | | response | parent.name | string \| null | n/a | Display name for the parent deployment. | | response | parent.handle | string \| null | n/a | Parent URL-safe handle. | | response | deployments[] | Deployment[] | n/a | Child deployments owned by the parent. | | response | count | number | n/a | Number of child deployments returned. | #### Python SDK Method: `prophet.deployments.list(parent_id: str | None = None) -> list[Deployment]` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | parent_id | str \| None | no | Parent MSP customer_id. Defaults to prophet.customer_id from the JWT aud claim. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | list[Deployment] | n/a | Flat SDK model list. Returns an empty list when no children exist. | | response | Deployment.customer_id | str | n/a | Child deployment customer_id. | | response | Deployment.name | str \| None | n/a | Child deployment display name. | | response | Deployment.handle | str \| None | n/a | Child deployment handle. | | response | Deployment.type | str \| None | n/a | Usually "child" for this API. | | response | Deployment.parent | str \| None | n/a | Parent MSP customer_id. | | response | Deployment.created_at | str \| None | n/a | ISO timestamp from the controller. | SDK example: ```python from prophet.sdk import Prophet prophet = Prophet( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", ) for deployment in prophet.deployments.list(): print(deployment.name, deployment.customer_id) ``` REST example: ```bash curl https://app.prophet.io/rest/deployments/1.0?parent_id=acme_msp \ -H "Authorization: Bearer $PROPHET_TOKEN" ``` Example response: ```json { "status": "success", "parent": { "customer_id": "acme_msp", "name": "Acme MSP", "handle": "acme_msp" }, "deployments": [ { "customer_id": "acme_msp-b5678c901", "name": "Client Alpha", "handle": "client_alpha", "type": "child", "parent": "acme_msp", "subdomain": "alpha", "deployment": { "status": "deployed" }, "created_at": "2026-02-04T16:00:00.000Z" } ], "count": 1 } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 403 | unauthorized | The bearer token customer is not the requested parent MSP. | #### SDK notes - The SDK calls GET /rest/deployments/1.0 and maps deployments[] into Deployment models. - prophet.deployments.get(customer_id) is an SDK convenience helper that lists and filters locally. ### POST /rest/deployments/1.0 — Create child deployment Docs URL: https://docs.prophet.io/#api-deployments-create Family: Deployments Auth: bearer token Scope: p.token.scope.deployment_api Version: 1.0 Stability: stable Creates a child deployment under the authenticated parent MSP. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | name | string | yes | Display name for the child deployment. | | body | handle | string | yes | URL-safe identifier. The controller slugifies it to lowercase with underscores. | | body | parent_id | string | yes | Parent MSP customer_id. Must match the authenticated parent customer. | | body | subdomain | string | no | Optional custom subdomain label for the child deployment. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | string | n/a | Always "success" on 201. | | response | deployment.customer.customer_id | string | n/a | Created child deployment customer_id. | | response | deployment.customer.name | string \| null | n/a | Created child deployment display name. | | response | deployment.customer.handle | string \| null | n/a | Slugified child handle. | | response | deployment.customer.type | string | n/a | Set to "child". | | response | deployment.customer.parent | string | n/a | Parent MSP customer_id. | | response | deployment.customer.org_code | string \| null | n/a | Organization code from the identity provider. | | response | deployment.org | object | n/a | Identity-provider organization summary returned by the create call. | #### Python SDK Method: `prophet.deployments.create(name: str, handle: str, parent_id: str | None = None) -> Deployment` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | name | str | yes | Display name. The SDK raises ValidationError when empty. | | response | handle | str | yes | URL-safe identifier. The SDK raises ValidationError when empty. | | response | parent_id | str \| None | no | Defaults to prophet.customer_id from the authenticated token. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | Deployment | n/a | The SDK unwraps deployment.customer from the REST response. | | response | Deployment.customer_id | str | n/a | Created child deployment customer_id. | | response | Deployment.org_code | str \| None | n/a | Identity-provider organization code, preserved as an extra model field. | | response | Deployment.deployment | dict[str, Any] \| None | n/a | Deployment status block, for example {"status": "deployed"}. | SDK example: ```python from prophet.sdk import Prophet prophet = Prophet( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", ) child = prophet.deployments.create( name="Acme Corp", handle="acme_corp", ) print(child.customer_id) ``` REST example: ```bash curl -X POST https://app.prophet.io/rest/deployments/1.0 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Acme Corp","handle":"acme_corp","parent_id":"parent-123"}' ``` Example response: ```json { "status": "success", "deployment": { "customer": { "customer_id": "acme_msp-d7890e123", "name": "Acme Corp", "handle": "acme_corp", "type": "child", "parent": "acme_msp", "subdomain": "acme", "org_code": "org_abc123def", "deployment": { "status": "deployed" }, "created_at": "2026-02-04T16:00:00.000Z" }, "org": { "code": "org_abc123def", "name": "Acme Corp", "handle": "acme_corp" } } } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 400 | missing_required_fields | name, handle, or parent_id is missing. | | 403 | unauthorized | The bearer token customer cannot create children under this parent_id, or the caller is not provisioned as a parent. | #### SDK notes - parent_id defaults to prophet.customer_id, so parent MSP callers usually omit it. - The SDK intentionally returns the flat Deployment model instead of the REST envelope. ### DELETE /rest/deployments/1.0 — Delete child deployment Docs URL: https://docs.prophet.io/#api-deployments-delete Family: Deployments Auth: bearer token Scope: p.token.scope.deployment_api Version: 1.0 Stability: stable Deletes a child deployment and its backing organization records. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | customer_id | string | yes | Child deployment customer_id to delete. | | body | parent_id | string | yes | Parent MSP customer_id. Must own the child deployment. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | string | n/a | Always "success" on 200. | | response | message | string | n/a | Human-readable deletion confirmation. | | response | deleted.customer_id | string | n/a | Deleted child deployment customer_id. | | response | deleted.name | string \| null | n/a | Deleted child deployment display name. | | response | deleted.handle | string \| null | n/a | Deleted child deployment handle. | #### Python SDK Method: `prophet.deployments.delete(customer_id: str, parent_id: str | None = None) -> None` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | customer_id | str | yes | Child deployment customer_id. The SDK raises ValidationError when empty. | | response | parent_id | str \| None | no | Defaults to prophet.customer_id from the authenticated token. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | None | n/a | The SDK validates success and returns None. | SDK example: ```python from prophet.sdk import Prophet prophet = Prophet( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", ) prophet.deployments.delete("acme_msp-d7890e123") ``` REST example: ```bash curl -X DELETE https://app.prophet.io/rest/deployments/1.0 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{"customer_id":"acme_msp-d7890e123","parent_id":"acme_msp"}' ``` Example response: ```json { "status": "success", "message": "Sub-deployment acme_msp-d7890e123 deleted successfully", "deleted": { "customer_id": "acme_msp-d7890e123", "name": "Acme Corp", "handle": "acme_corp" } } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 400 | missing_required_fields | customer_id or parent_id is missing. | | 403 | unauthorized | The bearer token customer cannot manage the requested parent_id. | | 404 | deployment_not_found | The child deployment was not found or does not belong to the parent. | #### SDK notes - The SDK sends customer_id and parent_id to DELETE /rest/deployments/1.0. - Deletion cascades through the controller path that emits the customer deletion hook. ## Nodes Provision units, inspect node health, trigger updates, and pull diagnostics. ### GET /rest/nodes/1.0 — List nodes Docs URL: https://docs.prophet.io/#api-nodes-list Family: Nodes Auth: bearer token Scope: p.token.scope.node_api Version: 1.0 Stability: stable Lists gRPC-managed nodes for the authenticated customer and child deployments. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | query | services | boolean | no | Include merged service configuration for each node. | | query | hardware | boolean | no | Include each node reported hardware block. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | nodes[] | Node[] | n/a | Tenant-scoped nodes for the caller and child deployments when the caller is a parent MSP. | | response | nodes[].connection | object | n/a | Derived control_plane and ingest connectivity booleans. | | response | nodes[].network | object | n/a | local_ip and public_ip reported by the node. | | response | nodes[].health | object | n/a | Last reported health status and timestamp. | | response | nodes[].created_at / updated_at | string | n/a | Record timestamps. | | response | nodes[].services | object \| omitted | n/a | Present only when services=true. | | response | nodes[].hardware | object \| omitted | n/a | Present only when hardware=true. | #### Python SDK Method: `prophet.nodes.list(*, services: bool = False, hardware: bool = False) -> list[Node]` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | services | bool | no | When True, request merged service config from the API. | | response | hardware | bool | no | When True, request reported hardware fields. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | list[Node] | n/a | Typed node models parsed from nodes[]. | | response | Node.node_id | str | n/a | Controller-assigned node identifier. | | response | Node.machine_id | str \| None | n/a | Stable machine identifier, often derived from a CPU ID. | | response | Node.customer_id | str | n/a | Tenant or child deployment that owns the node. | | response | Node.status | str \| None | n/a | Factory/enrollment state such as active, pending_approval, or staged. | | response | Node.connection.control_plane | bool | n/a | True when the control-plane connection is live or recently healthy. | | response | Node.connection.ingest | bool | n/a | True when ingest is connected and authenticated. | | response | Node.collector_version | str \| None | n/a | Reported collector version. | | response | Node.health.last_seen_at | str \| None | n/a | Last health report timestamp. | SDK example: ```python from prophet.sdk import Prophet prophet = Prophet( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", ) for node in prophet.nodes.list(services=True): if node.is_enrolled: print(node.node_id, node.customer_id, node.collector_version) ``` REST example: ```bash curl https://app.prophet.io/rest/nodes/1.0?services=true&hardware=true \ -H "Authorization: Bearer $PROPHET_TOKEN" ``` Example response: ```json { "nodes": [ { "node_id": "node-123", "machine_id": "8efc91d2-7f2d-529b-a59b-8f3b4e6b5c2a", "customer_id": "acme_msp-d7890e123", "customer_name": "Acme Corp", "description": "SN-0042", "status": "active", "profile_id": "prof-1", "profile_name": "Acme fleet", "connection": { "control_plane": true, "ingest": true }, "collector_version": "v0.3.0", "update_channel": "stable", "update_available": false, "latest_version": "v0.3.0", "health": { "status": "healthy", "last_seen_at": "2026-02-04T16:00:00.000Z" } } ] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | #### SDK notes - Node.is_active is true when status == "active". - Node.is_enrolled is true when the node is active and connection.control_plane is live. ### GET /rest/nodes/1.0/outdated — List outdated nodes Docs URL: https://docs.prophet.io/#api-nodes-outdated Family: Nodes Auth: bearer token Scope: p.token.scope.node_api Version: 1.0 Stability: stable Returns nodes whose collector version is behind the latest release for their channel. #### API inputs None documented. #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | count | number | n/a | Number of outdated nodes. | | response | nodes[] | object[] | n/a | Compact outdated-node view. | | response | nodes[].node_id | string | n/a | Node identifier. | | response | nodes[].collector_version | string | n/a | Current node version. | | response | nodes[].latest_version | string \| null | n/a | Latest version for the node update channel. | | response | nodes[].control_plane | boolean | n/a | Whether node can receive an update right now. | Example response: ```json { "count": 1, "nodes": [ { "node_id": "node-123", "customer_id": "acme_msp-d7890e123", "status": "active", "collector_version": "v0.2.0", "latest_version": "v0.3.0", "update_channel": "stable", "arch": "arm7", "control_plane": true, "health_status": "healthy" } ] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | ### GET /rest/nodes/1.0/:node_id — Get node Docs URL: https://docs.prophet.io/#api-nodes-get Family: Nodes Auth: bearer token Scope: p.token.scope.node_api Version: 1.0 Stability: stable Returns one node, scoped to the authenticated tenant tree. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | node_id | string | yes | Controller-assigned node identifier. | | query | services | boolean | no | Include merged service configuration. | | query | hardware | boolean | no | Include reported hardware block. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | node_id | string | n/a | Controller-assigned node identifier. | | response | machine_id | string \| null | n/a | Stable machine identifier. | | response | customer_id | string | n/a | Owning tenant or child deployment. | | response | customer_name / profile_name | string \| null | n/a | Display names for the owning tenant and applied profile. | | response | status | string \| null | n/a | active, pending_approval, staged, or future status value. | | response | network | object | n/a | local_ip and public_ip reported by the node. | | response | connection | object | n/a | control_plane and ingest connectivity booleans. | | response | health | object | n/a | Health status and last_seen_at. | | response | created_at / updated_at | string | n/a | Record timestamps. | #### Python SDK Method: `prophet.nodes.get(node_id: str, *, services: bool = False, hardware: bool = False) -> Node | None` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | node_id | str | yes | Controller-assigned node identifier. The SDK raises ValidationError when empty. | | response | services | bool | no | Request merged service config. | | response | hardware | bool | no | Request hardware block. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | Node \| None | n/a | Returns None when the API responds 404. | | response | Node.node_id | str | n/a | Controller-assigned node identifier. | | response | Node.machine_id | str \| None | n/a | Stable machine identifier, often derived from a CPU ID. | | response | Node.customer_id | str | n/a | Tenant or child deployment that owns the node. | | response | Node.status | str \| None | n/a | Factory/enrollment state such as active, pending_approval, or staged. | | response | Node.connection.control_plane | bool | n/a | True when the control-plane connection is live or recently healthy. | | response | Node.connection.ingest | bool | n/a | True when ingest is connected and authenticated. | | response | Node.collector_version | str \| None | n/a | Reported collector version. | | response | Node.health.last_seen_at | str \| None | n/a | Last health report timestamp. | SDK example: ```python node = prophet.nodes.get("node-123", services=True) if node is None: print("node not found") elif node.is_enrolled: print("node is enrolled and collecting") ``` REST example: ```bash curl https://app.prophet.io/rest/nodes/1.0/node-123?services=true \ -H "Authorization: Bearer $PROPHET_TOKEN" ``` Example response: ```json { "node_id": "node-123", "machine_id": "8efc91d2-7f2d-529b-a59b-8f3b4e6b5c2a", "customer_id": "acme_msp-d7890e123", "status": "active", "connection": { "control_plane": true, "ingest": true }, "collector_version": "v0.3.0", "health": { "status": "healthy", "last_seen_at": "2026-02-04T16:00:00.000Z" } } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 404 | not_found | Node is missing or outside the caller tenant tree. Body is {"error":"Node not found","error_type":"not_found"}. | #### SDK notes - The controller intentionally returns 404 for missing nodes and nodes outside the caller tenant tree. - Use prophet.nodes.find_by_machine_id(machine_id) when the node_id is not known before first boot. ### GET /rest/nodes/1.0/:node_id/diagnostics — Pull node diagnostics Docs URL: https://docs.prophet.io/#api-nodes-diagnostics Family: Nodes Auth: bearer token Scope: p.token.scope.node_api Version: 1.0 Stability: stable Fetches supervisor diagnostics and recent logs from a connected node. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | node_id | string | yes | Node identifier. | | query | log_lines | number | no | Recent worker log lines to return. Controller clamps this value. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | node_id | string | n/a | Node identifier. | | response | diagnostics | object \| null | n/a | Supervisor diagnostics report. | | response | recent_logs | string[] | n/a | Recent worker log lines. | Example response: ```json { "node_id": "node-123", "diagnostics": { "worker_status": "running", "memory_budget_mb": 256 }, "recent_logs": [ "collector worker healthy" ] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 404 | not_found | Node is missing or outside the caller tenant tree. | | 409 | not_connected | Node control plane is not connected. | | 504 | diagnostic_pull_failed | Supervisor request timed out or failed. | ### GET /rest/nodes/1.0/:node_id/pprof — Capture pprof profile Docs URL: https://docs.prophet.io/#api-nodes-pprof Family: Nodes Auth: bearer token Scope: p.token.scope.node_api Version: 1.0 Stability: stable Streams a raw gzipped pprof profile for connected nodes. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | node_id | string | yes | Node identifier. | | query | type | "HEAP" \| "ALLOCS" \| "GOROUTINE" \| "CPU" \| "BLOCK" \| "MUTEX" | no | Profile type. Defaults to HEAP. | | query | duration | number | no | Duration seconds for CPU/BLOCK/MUTEX captures; capped by controller. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | Content-Type | application/octet-stream | n/a | Raw gzipped pprof profile bytes. | | response | Content-Disposition | header | n/a | Attachment filename formatted as -.pprof. | Example response: ```json HTTP/1.1 200 OK Content-Type: application/octet-stream Content-Disposition: attachment; filename="node-123-heap.pprof" ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 400 | invalid_type | Profile type is not supported. | | 404 | not_found | Node is missing or outside the caller tenant tree. | | 409 | not_connected | Node control plane is not connected. | | 504 | pprof_capture_failed | Profile capture timed out or failed. | ### POST /rest/nodes/1.0/:node_id/update — Update node Docs URL: https://docs.prophet.io/#api-nodes-update Family: Nodes Auth: bearer token Scope: p.token.scope.node_api Version: 1.0 Stability: stable Sends the latest collector release to a connected gRPC-managed node. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | node_id | string | yes | Node identifier. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | node_id | string | n/a | Node identifier. | | response | update_status | "update_sent" \| "already_current" | n/a | Update command result from the node handler. | | response | version | string | n/a | Latest release version for the node channel. | | response | platform | string \| omitted | n/a | Release platform (os_arch) — present when update_status is update_sent. | Example response: ```json { "node_id": "node-123", "update_status": "update_sent", "version": "v0.3.0", "platform": "linux_arm7" } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 404 | not_found | Node is missing or outside the caller tenant tree. | | 409 | not_connected | Node control plane is not connected. | | 502 | update_failed | Node update command failed. | ### POST /rest/nodes/provision/1.0 — Provision unit credential Docs URL: https://docs.prophet.io/#api-nodes-provision Family: Nodes Auth: bearer token Scope: p.token.scope.node_api Version: 1.0 Stability: stable Mints a per-unit collector credential for a target deployment. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | customer_id | string | yes | Target deployment customer_id. Can be the caller itself or one of its children. | | body | machine_id | string | no | Stable machine identifier. The SDK can derive this from cpu_id. | | body | description | string | no | Human label stored on the generated credential. | | body | profile_id | string | no | Profile to inherit at first boot. The controller rejects unknown profile_id values. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | string | n/a | Always "success" on 201. | | response | access_key | string | n/a | One-time collector credential in client_id.client_secret form. | | response | customer_id | string | n/a | Target deployment customer_id. | | response | machine_id | string \| null | n/a | Machine identifier echoed from the request. | | response | profile_id | string \| null | n/a | Profile associated with this credential. | #### Python SDK Method: `prophet.nodes.provision(deployment: str, cpu_id: str | None = None, *, machine_id: str | None = None, description: str | None = None, profile_id: str | None = None) -> ProvisionedUnit` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | deployment | str | yes | Target deployment customer_id. Sent to REST as customer_id. | | response | cpu_id | str \| None | no | Hardware CPU ID. The SDK derives a deterministic machine_id from it. | | response | machine_id | str \| None | no | Explicit machine_id. Overrides cpu_id derivation. | | response | description | str \| None | no | Human label for the credential. | | response | profile_id | str \| None | no | Capture profile to inherit at first boot. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | ProvisionedUnit | n/a | Immutable SDK result containing the one-time access_key and bootstrap helpers. | | response | ProvisionedUnit.access_key | str | n/a | One-time client_id.client_secret credential. Secret-safe repr never prints the secret half. | | response | ProvisionedUnit.machine_id | str | n/a | Derived or explicit machine_id. | | response | ProvisionedUnit.customer_id | str | n/a | Target deployment customer_id. | | response | ProvisionedUnit.profile_id | str \| None | n/a | Profile associated with this unit. | | response | ProvisionedUnit.collector_yaml(...) | str | n/a | Renders prophet_collector.yaml content for the unit. | SDK example: ```python unit = prophet.nodes.provision( deployment="acme_msp-d7890e123", cpu_id="0x1122334455667788", description="SN-0042", profile_id="prof-1", ) device_yaml = unit.collector_yaml( env="prod", spool_dir="/data/apps/prophet/spool", ) ``` REST example: ```bash curl -X POST https://app.prophet.io/rest/nodes/provision/1.0 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{"customer_id":"child-123","machine_id":"board-cpu-id","profile_id":"profile-uuid"}' ``` Example response: ```json { "status": "success", "access_key": "clientid.secretpart", "customer_id": "acme_msp-d7890e123", "machine_id": "8efc91d2-7f2d-529b-a59b-8f3b4e6b5c2a", "profile_id": "prof-1" } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 400 | missing_required_fields | customer_id is missing. | | 403 | unauthorized | Target deployment is not owned by the authenticated parent. | | 404 | deployment_not_found | Target deployment does not exist. | | 404 | profile_not_found | profile_id does not exist. | #### SDK notes - The SDK requires deployment and either cpu_id or machine_id before it sends the request. - prophet.factory.build(...) composes this provisioning call with collector download and installer bundle generation. ### POST /rest/nodes/1.0/:node_id/manage — Legacy deploy-node manage Docs URL: https://docs.prophet.io/#api-nodes-manage Family: Nodes Auth: bearer token Scope: p.token.scope.node_api Version: 1.0 Stability: legacy Legacy operations for first-generation deploy nodes. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | node_id | string | yes | Legacy deploy node identifier. | | body | operation | "status" \| "logs" \| "pull" \| "restart" \| "upgrade" | yes | Legacy socket operation to run. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | node_id | string | n/a | Legacy deploy node identifier. | | response | operation | string | n/a | Operation that was requested. | | response | result | object | n/a | Socket response from the legacy deploy node. | Example response: ```json { "node_id": "legacy-node-123", "operation": "status", "result": { "status": "ok" } } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 400 | unknown_operation | operation is not one of status, logs, pull, restart, upgrade. | | 404 | not_connected | Legacy deploy node is not connected. | | 504 | timeout | Legacy socket operation timed out. | ## Profiles Reusable collector capture configuration for fleets and child deployments. ### GET /rest/profiles/1.0 — List profiles Docs URL: https://docs.prophet.io/#api-profiles-list Family: Profiles Auth: bearer token Scope: p.token.scope.node_api Version: 1.0 Stability: stable Lists reusable node capture profiles for the authenticated tenant tree. #### API inputs None documented. #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | string | n/a | Always "success" on 200. | | response | profiles[] | Profile[] | n/a | Profiles owned by the caller and child deployments when caller is a parent MSP. | | response | count | number | n/a | Number of profiles returned. | #### Python SDK Method: `prophet.profiles.list() -> list[Profile]` None documented. SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | list[Profile] | n/a | Typed profile models parsed from profiles[]. | | response | Profile.profile_id | str | n/a | Reusable capture profile identifier. | | response | Profile.customer_id | str | n/a | Tenant that owns the profile. | | response | Profile.name | str | n/a | Display name. | | response | Profile.description | str \| None | n/a | Optional notes. | | response | Profile.services | dict[str, Any] \| None | n/a | Per-service capture config (packet, host_logs, netflow, suricata_logs). Omitted fields use server defaults. See the Collector config section for the full schema, defaults, and gotchas. | | response | Profile.tags | list[str] \| None | n/a | Optional tags applied to nodes using this profile. | | response | Profile.update_channel | "stable" \| "dev" \| "pinned" \| str \| None | n/a | Collector update channel. | | response | Profile.fleet_staging | bool \| None | n/a | Whether provision-token nodes start staged. | SDK example: ```python for profile in prophet.profiles.list(): print(profile.profile_id, profile.name) ``` REST example: ```bash curl https://app.prophet.io/rest/profiles/1.0 \ -H "Authorization: Bearer $PROPHET_TOKEN" ``` Example response: ```json { "status": "success", "profiles": [ { "profile_id": "prof-1", "customer_id": "acme_msp", "name": "Acme fleet", "services": { "packet": { "enabled": true, "lightweight": true } }, "update_channel": "stable", "fleet_staging": false } ], "count": 1 } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | #### SDK notes - A parent MSP can list profiles owned by itself and child deployments. - Use profile.profile_id when provisioning units with prophet.nodes.provision(...). ### POST /rest/profiles/1.0 — Create profile Docs URL: https://docs.prophet.io/#api-profiles-create Family: Profiles Auth: bearer token Scope: p.token.scope.node_api Version: 1.0 Stability: stable Creates a reusable capture configuration profile. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | name | string | yes | Profile display name. | | body | description | string | no | Optional notes for operators. | | body | services | object | no | Capture-config service blocks. A submitted service block is stored as-is; omitted services and fields fall back to server defaults when the profile is delivered to a node. | | body | tags | string[] | no | Tags applied to nodes using this profile. | | body | update_channel | "stable" \| "dev" \| "pinned" | no | Collector update channel. SDK default is stable. | | body | fleet_staging | boolean | no | When true, provision-token nodes start staged. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | string | n/a | Always "success" on 201. | | response | profile | Profile | n/a | Created profile document. Each submitted service block is echoed as-is; per-field defaults are applied when the profile is delivered to a node, not at create time. | #### Python SDK Method: `prophet.profiles.create(name: str, *, description: str | None = None, services: ProfileServices | dict[str, Any] | None = None, tags: list[str] | None = None, update_channel: Literal["stable", "dev", "pinned"] = "stable", fleet_staging: bool = False) -> Profile` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | name | str | yes | Profile display name. The SDK raises ValidationError when empty. | | response | description | str \| None | no | Optional notes. | | response | services | ProfileServices \| dict[str, Any] \| None | no | Typed service config or raw dict escape hatch. | | response | tags | list[str] \| None | no | Optional node tags. | | response | update_channel | "stable" \| "dev" \| "pinned" | no | SDK default is stable. | | response | fleet_staging | bool | no | SDK default is False. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | Profile | n/a | Typed SDK model from response.profile. | | response | Profile.profile_id | str | n/a | Reusable capture profile identifier. | | response | Profile.customer_id | str | n/a | Tenant that owns the profile. | | response | Profile.name | str | n/a | Display name. | | response | Profile.description | str \| None | n/a | Optional notes. | | response | Profile.services | dict[str, Any] \| None | n/a | Per-service capture config (packet, host_logs, netflow, suricata_logs). Omitted fields use server defaults. See the Collector config section for the full schema, defaults, and gotchas. | | response | Profile.tags | list[str] \| None | n/a | Optional tags applied to nodes using this profile. | | response | Profile.update_channel | "stable" \| "dev" \| "pinned" \| str \| None | n/a | Collector update channel. | | response | Profile.fleet_staging | bool \| None | n/a | Whether provision-token nodes start staged. | SDK example: ```python from prophet.sdk.profiles import lightweight_packet_services profile = prophet.profiles.create( name="Acme fleet", services=lightweight_packet_services(interface_patterns=["eth*"]), tags=["edge"], ) print(profile.profile_id) ``` REST example: ```bash curl -X POST https://app.prophet.io/rest/profiles/1.0 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Acme fleet","services":{"packet":{"enabled":true,"lightweight":true}}}' ``` Example response: ```json { "status": "success", "profile": { "profile_id": "prof-1", "customer_id": "acme_msp", "name": "Acme fleet", "services": { "packet": { "enabled": true, "lightweight": true } }, "update_channel": "stable", "auto_update": false, "fleet_staging": false, "created_at": "2026-02-04T16:00:00.000Z", "updated_at": "2026-02-04T16:00:00.000Z" } } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 400 | missing_required_fields | name is missing. | #### SDK notes - ProfileServices omits unset fields; per-field defaults are applied when the profile is delivered to a node. - extra="forbid" in the typed service models catches misspelled service fields before the request is sent. ### DELETE /rest/profiles/1.0/:profile_id — Delete profile Docs URL: https://docs.prophet.io/#api-profiles-delete Family: Profiles Auth: bearer token Scope: p.token.scope.node_api Version: 1.0 Stability: stable Deletes a profile owned by the authenticated tenant tree. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | profile_id | string | yes | Profile identifier to delete. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | string | n/a | Always "success" on 200. | | response | deleted.profile_id | string | n/a | Deleted profile identifier. | #### Python SDK Method: `prophet.profiles.delete(profile_id: str) -> None` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | profile_id | str | yes | Profile identifier. The SDK raises ValidationError when empty. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | None | n/a | The SDK validates success and returns None. | SDK example: ```python prophet.profiles.delete("prof-1") ``` REST example: ```bash curl -X DELETE https://app.prophet.io/rest/profiles/1.0/prof-1 \ -H "Authorization: Bearer $PROPHET_TOKEN" ``` Example response: ```json { "status": "success", "deleted": { "profile_id": "prof-1" } } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 404 | not_found | Profile is missing or outside the caller tenant tree. | #### SDK notes - The API only deletes profiles owned by the authenticated tenant tree. ## Collector Download binaries and install or uninstall the Prophet collector. ### GET /rest/collector/download/1.0 — Download collector binary Docs URL: https://docs.prophet.io/#api-collector-download Family: Collector Auth: none Scope: public Version: 1.0 Stability: stable Redirects to a signed GitHub release asset for the latest collector binary. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | query | os | "linux" \| "darwin" \| "windows" | no | Target operating system. Defaults to linux. Supported os/arch pairs: linux/amd64, linux/arm7, darwin/arm64, windows/amd64. | | query | arch | "amd64" \| "arm7" \| "arm64" | no | Target architecture. Defaults to amd64. Only the four supported os/arch pairs are valid — there is no linux/arm64 build. | | query | channel | "stable" \| "dev" | no | Release channel. Defaults to stable in production, dev in dev. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | 302 Location | URL | n/a | Temporary signed release asset URL. | | response | Content-Disposition | header | n/a | Versioned filename when the signed asset response is followed. | | response | body | application/octet-stream | n/a | Release tarball containing the prophet binary. | #### Python SDK Method: `prophet.collector.download(dest: str | Path | None = None, *, os: OS = "linux", arch: Arch = "amd64", channel: Channel = "stable", extract: bool = False, cache: bool = True) -> Path` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | dest | str \| Path \| None | no | Tarball path, or output directory when extract=True. | | response | os | "linux" \| "darwin" \| "windows" | no | SDK default is linux. | | response | arch | "amd64" \| "arm7" \| "arm64" | no | SDK default is amd64. | | response | channel | "stable" \| "dev" | no | SDK default is stable. | | response | extract | bool | no | When True, unpack the archive and return the prophet binary path. | | response | cache | bool | no | Reuse cached versioned binaries. Set False to force a fresh fetch. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | download_url(...) | str | n/a | Controller URL that redirects to the latest signed binary. | | response | download(...) | Path | n/a | Path to downloaded tarball, or extracted prophet binary when extract=True. | SDK example: ```python # URL only, useful for install scripts. url = prophet.collector.download_url(arch="arm7") # Download and extract the latest stable ARM7 collector. binary = prophet.collector.download( arch="arm7", extract=True, dest="./dist", ) print(binary) ``` REST example: ```bash curl -L "https://app.prophet.io/rest/collector/download/1.0?os=linux&arch=arm7&channel=stable" \ -o prophet_collector_linux_arm7.tar.gz ``` Example response: ```json HTTP/1.1 302 Found Location: https://github-releases.githubusercontent.com/signed/asset/url # Follow the redirect to receive a versioned .tar.gz collector archive. ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 400 | error | Unsupported os/arch pair. Body is {"error":"Unsupported platform: /","supported":[...]} listing the valid pairs. | | 404 | error | No release, no assets, or no binary for the requested channel/platform. Body is {"error":""}. | | 500 | error | Release lookup failed. Body is {"error":""}. | #### SDK notes - The SDK caches downloads by versioned Content-Disposition filename under ~/.cache/prophet-sdk/collector (override the root with PROPHET_SDK_CACHE_DIR). - When extract=True, the SDK unpacks prophet safely and chmods it executable. - prophet.factory.build(...) uses this API while assembling an install bundle. ### GET /rest/collector/install/1.0 — Get install script Docs URL: https://docs.prophet.io/#api-collector-install Family: Collector Auth: none Scope: public Version: 1.0 Stability: stable Returns a Bash installer for Linux and macOS collectors. #### API inputs None documented. #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | Content-Type | text/plain; charset=utf-8 | n/a | Bash installer script. | | response | Content-Disposition | inline | n/a | Script is intended to be piped to a shell or reviewed inline. | | response | body | bash | n/a | Detects OS/architecture, downloads the collector, installs the binary, and configures systemd or launchd. | REST example: ```bash curl -sSL https://app.prophet.io/rest/collector/install/1.0 \ | sudo bash -s -- "$PROPHET_PROVISION_TOKEN" ``` Example response: ```json HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Content-Disposition: inline #!/bin/bash set -e # Prophet Collector Installer # Usage: curl -sSL https://app.prophet.io/rest/collector/install/1.0 | bash -s -- ``` ### GET /rest/collector/uninstall/1.0 — Get uninstall script Docs URL: https://docs.prophet.io/#api-collector-uninstall Family: Collector Auth: none Scope: public Version: 1.0 Stability: stable Returns a Bash uninstaller for collector service and local files. #### API inputs None documented. #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | Content-Type | text/plain; charset=utf-8 | n/a | Bash uninstall script. | | response | Content-Disposition | inline | n/a | Script is intended to be piped to a shell or reviewed inline. | | response | body | bash | n/a | Stops and removes systemd or launchd service files, removes the collector binary, and removes local configuration. | REST example: ```bash curl -sSL https://app.prophet.io/rest/collector/uninstall/1.0 \ | sudo bash ``` Example response: ```json HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Content-Disposition: inline #!/bin/bash set -e # Prophet Collector Uninstaller ``` ## Search Query flow records, request timeseries buckets, and run terms aggregations over flow data. ### POST /search/records/1.0 — Query flow records Docs URL: https://docs.prophet.io/#api-flows-query Family: Search Auth: bearer token Scope: p.token.scope.search_api Version: 1.0 Stability: stable Queries flow records for one or more instances with PQL, time filters, sorting, field selection, pagination, and optional timeseries buckets. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | instance_ids | string[] | yes | Instances or customer IDs to search. | | body | module | "flows" | yes | Must be flows for flow record search. | | body | sentence | string | no | PQL query. Empty means match all. | | body | start | DateTimeFilter | no | Start time filter. Defaults to relative 15 minutes. | | body | end | DateTimeFilter | no | End time filter. Defaults to now. | | body | sort | { field: string, order: "asc" \| "desc" }[] | no | Sort entries, applied in order. Defaults to @timestamp desc. | | body | fields | string[] | no | Fields to include. Missing dotted paths are omitted; wildcard "*" is rejected. @timestamp is always included; the injected record id is dropped unless requested. | | body | size | number | no | Page size. Default is 15 in REST, SDK default is 100, maximum is 25000. size=0 is only valid with timeseries. | | body | page | number | no | Zero-based page number. Offset is page * size. | | body | timeseries.buckets | number | no | Auto date histogram bucket count. Use when interval is not supplied. | | body | timeseries.interval | string | no | Fixed date histogram interval such as 30s, 5m, 1h, or 1d. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | .flows[] | object[] | n/a | Flow records. With multiple instance_ids the search runs across all of them merged, and each instance key carries the same merged result set. Each record carries an injected id (the backing document id) unless a fields projection is used. | | response | .id | string | n/a | Opaque per-response identifier; not stable across requests. | | response | .found | number | n/a | Matching document count. | | response | .total | number | n/a | Total record count for the searched window. | | response | .returned | number | n/a | Records returned in this page. | | response | .page_size | number | n/a | Page size used by this response. | | response | .current_page | number | n/a | Currently always 0 regardless of the requested page; track pagination from your request. Offset math itself honors the requested page. | | response | .next_page | number | n/a | 1 when more_data_available, otherwise 0. | | response | .pages | number | n/a | Total page count: found divided by page_size, rounded up. | | response | .more_data_available | boolean | n/a | True when found exceeds page_size. Not page-aware: it stays true on the last page of a large result. | | response | .count_aggregation[] | object[] \| omitted | n/a | Timeseries buckets with time and count when timeseries is requested. | | response | .interval | string \| omitted | n/a | Histogram interval returned for timeseries queries. | | response | .interval_period | string \| omitted | n/a | Unit suffix of the histogram interval (for example h for hours). | | response | .took | number | n/a | Search execution time in seconds. | | response | results_for[] | string[] | n/a | Instance IDs searched. | | response | total_time_ms | number | n/a | End-to-end handler execution time. | | response | success | boolean | n/a | True when the request completed. | #### Python SDK Method: `prophet.flows.query(instance: str, query: str | Q = "", start: TimeFilter | None = None, end: TimeFilter | None = None, sort: list[Sort] | None = None, fields: list[str] | None = None, size: int = 100) -> FlowIterator` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | instance | str | yes | Single instance/customer ID. SDK sends instance_ids: [instance]. | | response | query | str \| Q | no | Raw PQL string or fluent Q builder. | | response | start | TimeFilter \| None | no | Now, MinutesAgo, HoursAgo, DaysAgo, WeeksAgo, or At. | | response | end | TimeFilter \| None | no | End time filter. | | response | sort | list[Sort] \| None | no | Sort objects converted to API sort payloads. | | response | fields | list[str] \| None | no | Response fields to include. | | response | size | int | no | Page size. SDK default is 100. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | FlowIterator | n/a | Lazy iterator that auto-fetches pages as you iterate. | | response | FlowIterator.take(n) | FlowIterator | n/a | Limit total results across pages. | | response | FlowIterator.first() | FlowPage | n/a | Fetch only the first page. | | response | FlowIterator.collect(limit=None) | list[Flow] | n/a | Collect results eagerly, optionally bounded by limit. | | response | FlowIterator.next_page() | FlowPage \| None | n/a | Fetch the next page directly; None once exhausted. | | response | FlowIterator.total_found | int \| None | n/a | Matching document count; None before the first fetch. | | response | FlowPage.flows | list[Flow] | n/a | Typed flow models for the current page. | | response | FlowPage.found | int | n/a | Total matching documents. | | response | FlowPage.total | int | n/a | Index-wide record count returned by the search service. | | response | FlowPage.returned | int | n/a | Flow records returned in this page. | | response | FlowPage.current_page | int | n/a | Zero-based page number. | | response | FlowPage.page_count | int | n/a | Total page count reported by search. | | response | FlowPage.has_more | bool | n/a | True when another page can be fetched. | | response | FlowPage.took | float | n/a | Search execution time in seconds. | | response | Flow.src.ip / Flow.dst.ip | str \| None | n/a | Structured source and destination endpoint addresses. | | response | Flow.src_ip / Flow.dst_ip | str | n/a | Convenience properties for source and destination addresses. | | response | Flow.bytes / Flow.packets | float | n/a | Convenience totals derived from metric or stats fields. | | response | Flow.protocol | str | n/a | Transport protocol convenience value. | SDK example: ```python from prophet.sdk import Q, HoursAgo, Now, Sort flows = prophet.flows.query( instance="acme_msp-d7890e123", query=Q("dst.port").eq(443), start=HoursAgo(24), end=Now(), sort=[Sort("@timestamp", "desc")], fields=[ "src.ip", "dst.ip", "stats.volume.bytes.total", "app_name", ], size=100, ) for flow in flows.take(500): print(flow.src.ip, flow.dst.ip, flow.bytes, flow.app_name) ``` REST example: ```bash curl -X POST https://app.prophet.io/search/records/1.0 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "instance_ids": ["acme_msp-d7890e123"], "module": "flows", "sentence": "dst.port eq 443", "start": { "relative": { "value": 24, "unit": "hours" } }, "end": { "now": true }, "size": 100, "page": 0, "timeseries": { "interval": "1h" } }' ``` Example response: ```json { "acme_msp-d7890e123": { "flows": [ { "src": { "ip": "10.0.0.10", "port": 51244 }, "dst": { "ip": "198.51.100.10", "port": 443 }, "transport": { "proto": "TCP" }, "stats": { "volume": { "bytes": { "total": 1024 } } } } ], "found": 4183, "total": 88421, "returned": 100, "page_size": 100, "current_page": 0, "next_page": 1, "pages": 42, "more_data_available": true, "count_aggregation": [ { "time": 1770220800, "count": 18 }, { "time": 1770224400, "count": 24 } ], "interval": "1h", "interval_period": "h", "took": 0.15 }, "results_for": ["acme_msp-d7890e123"], "total_time_ms": 151, "success": true } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 400 | validation_error | Invalid body, module, instance_ids, date filter, sort, fields, size/page, or PQL parsing failure. | | 500 | search_error | Search execution failed. | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | #### SDK notes - prophet.flows(...) is a shortcut for prophet.flows.query(...). - The SDK maps each flow record into a tolerant Flow model with dot-access fields such as flow.src.ip, flow.dst.ip, flow.transport.proto, and flow.stats.volume.bytes.total. - Shortcut properties such as flow.src_ip, flow.dst_ip, flow.protocol, flow.bytes, and flow.packets are available for common reads. - The current Python SDK covers records search and pagination. Terms aggregation is REST-only today. - 400 search validation errors are raised as APIError with error_type="validation_error". ### POST /search/agg/1.0 — Aggregate flow terms Docs URL: https://docs.prophet.io/#api-flows-terms-agg Family: Search Auth: bearer token Scope: p.token.scope.search_api Version: 1.0 Stability: stable Groups matching flows by a field and computes sum, average, or cardinality metrics for each bucket. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | module | "terms_agg" | yes | Must be terms_agg for the aggregation endpoint. | | body | instance_id | string | yes | Single instance/customer ID to aggregate. | | body | sentence | string | no | PQL query. Empty means match all. | | body | start | DateTimeFilter | no | Start time filter. Defaults to relative 15 minutes. | | body | end | DateTimeFilter | no | End time filter. Defaults to now. | | body | size | number | no | Maximum buckets. Defaults to 100; max is 10000. | | body | agg.field | string | yes | Field to group by. Must be an aggregatable (keyword-mapped) field; free-text fields fail. | | body | agg.by[].field | string | yes | Metric field to aggregate. | | body | agg.by[].metric | "sum" \| "avg" \| "cardinality" | yes | Metric aggregation type. | | body | agg.by[].order | "asc" \| "desc" | yes | Bucket ordering for this metric. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | "success" \| "error" | n/a | Aggregation status. | | response | results.aggregation[] | object[] | n/a | Buckets. Each bucket flattens the grouped field, count, requested metrics, and an optional sample flow. | | response | results.aggregation[].count | number | n/a | Document count in the bucket. | | response | results.types | Record | n/a | Simple metric type metadata such as bytes, packets, duration, or integer. | | response | results.flows[] | object[] | n/a | Sample flow documents, one per bucket when available. | | response | results.found | number | n/a | Total matching documents. | | response | results.total_distinct | number | n/a | Cardinality of the grouped field. | | response | results.returned | number | n/a | Buckets returned. | | response | customer_id | string | n/a | Echoed instance/customer ID. | | response | errors | string \| omitted | n/a | Error text when status is error. | REST example: ```bash curl -X POST https://app.prophet.io/search/agg/1.0 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "module": "terms_agg", "instance_id": "acme_msp-d7890e123", "sentence": "dst.port eq 443", "size": 10, "start": { "relative": { "value": 24, "unit": "hours" } }, "end": { "now": true }, "agg": { "field": "dst.ip", "by": [ { "field": "stats.volume.bytes.total", "metric": "sum", "order": "desc" }, { "field": "src.ip", "metric": "cardinality", "order": "desc" } ] } }' ``` Example response: ```json { "status": "success", "results": { "aggregation": [ { "dst.ip": "198.51.100.10", "count": 37, "stats.volume.bytes.total": 52428800, "src.ip": 18, "flow": { "src": { "ip": "10.0.0.10" }, "dst": { "ip": "198.51.100.10", "port": 443 } } } ], "types": { "stats.volume.bytes.total": "integer", "src.ip": "integer" }, "flows": [ { "src": { "ip": "10.0.0.10" }, "dst": { "ip": "198.51.100.10", "port": 443 } } ], "found": 420, "total_distinct": 73, "returned": 1 }, "customer_id": "acme_msp-d7890e123" } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 400 | error | Validation failure. Body is {"status":"error","errors":""} — text prefixed with Invalid request body, Request validation failed, Invalid date filter, or PQL parsing failed. | | 500 | error | Aggregation failure. Body is {"status":"error","errors":"Terms aggregation failed: "}. | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | ## Investigations Read the finished investigations Prophet produces when breach signal appears — verdict, key findings, provenance lineage across access, execution, and network, and recommended actions. One question is left for a human: was this authorized? ### GET /rest/investigations/1.0 — List investigations Docs URL: https://docs.prophet.io/#api-investigations-list Family: Investigations Auth: bearer token Scope: p.token.scope.investigations_api Version: 1.0 Stability: stable Lists Apollo investigation rollups for the authenticated tenant, filterable by verdict, confidence, and time, most severe first. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | query | disposition | "benign" \| "malicious" \| "escalate" | no | Filter by Apollo's verdict. | | query | min_confidence | number | no | Only verdicts with confidence >= this (0..1). | | query | since | string (ISO-8601) | no | Only investigations created at/after this time. | | query | until | string (ISO-8601) | no | Only investigations created at/before this time. | | query | sort | "severity" \| "recent" | no | Order. Default severity (most severe first). | | query | limit | number | no | Page size, clamped to 1..200. Default 50. | | query | offset | number | no | Page offset. Default 0. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | investigations[] | InvestigationListItem[] | n/a | Compact rollup rows for the current page. | | response | investigations[].id | string | n/a | Investigation id. Pass to GET /rest/investigations/1.0/{id}. | | response | investigations[].status | "running" \| "completed" \| "failed" | n/a | Lifecycle state. | | response | investigations[].disposition | "benign" \| "malicious" \| "escalate" \| null | n/a | Apollo's verdict. null while running. | | response | investigations[].confidence | number \| null | n/a | Verdict confidence 0..1. null while running. | | response | investigations[].headline | string \| null | n/a | One-line plain-language summary of the investigation. | | response | investigations[].source | string \| null | n/a | Originating entity of the flagged activity. | | response | investigations[].destination | string \| null | n/a | Counterparty of the flagged activity. | | response | investigations[].detected_at | string \| null | n/a | When the triggering activity was flagged. | | response | investigations[].created_at | string \| null | n/a | When Apollo opened the investigation. | | response | investigations[].completed_at | string \| null | n/a | When Apollo closed it. null while running. | | response | investigations[].related_alerts_count | number | n/a | Near-duplicate detections auto-linked into this one investigation. | | response | investigations[].tags | string[] | n/a | Free-form labels applied to the investigation. | | response | total | number | n/a | Total investigations matching the filter (for pagination). | | response | limit | number | n/a | Page size used by this response. | | response | offset | number | n/a | Page offset used by this response. | #### Python SDK Method: `prophet.investigations.list(*, disposition: str | None = None, min_confidence: float | None = None, since: str | datetime | None = None, until: str | datetime | None = None, sort: str | None = None, limit: int = 50, offset: int = 0) -> InvestigationPage` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | disposition | str \| None | no | Filter by verdict: benign, malicious, or escalate. | | response | min_confidence | float \| None | no | Only verdicts with confidence >= this (0..1). | | response | since | str \| datetime \| None | no | Created-at lower bound. A datetime is serialized to ISO-8601. | | response | until | str \| datetime \| None | no | Created-at upper bound. | | response | sort | str \| None | no | "severity" (default) or "recent". | | response | limit | int | no | Page size (default 50, max 200). | | response | offset | int | no | Page offset (default 0). | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | InvestigationPage | n/a | Iterable page of InvestigationListItem, with .total, .has_more, and .items. | | response | InvestigationListItem.id | str | n/a | Investigation id. | | response | InvestigationListItem.disposition | str \| None | n/a | benign \| malicious \| escalate (None while running). | | response | InvestigationListItem.confidence | float \| None | n/a | Verdict confidence. | | response | InvestigationListItem.headline | str \| None | n/a | One-line summary. | | response | InvestigationListItem.is_malicious / .needs_escalation / .is_running | bool | n/a | Convenience checks over disposition/status. | SDK example: ```python from prophet.sdk import Prophet prophet = Prophet( client_id="YOUR_CLIENT_ID", client_secret="YOUR_CLIENT_SECRET", ) # Newest escalations first page = prophet.investigations.list(disposition="escalate", sort="recent", limit=25) print(page.total) for inv in page: print(inv.headline, inv.confidence) # Stream every match across all pages (auto-pagination) for inv in prophet.investigations.iter(disposition="malicious"): print(inv.id, inv.confidence) ``` REST example: ```bash curl -G https://app.prophet.io/rest/investigations/1.0 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -d "disposition=escalate" -d "sort=recent" -d "limit=25" ``` Example response: ```json { "investigations": [ { "id": "inv_43d67a...", "status": "completed", "disposition": "escalate", "confidence": 0.85, "headline": "Escalate: a first-time 616 MB export to Google Drive under valid credentials; intent unconfirmed.", "source": "10.90.8.16", "destination": "google", "detected_at": "2026-06-29T20:00:00Z", "created_at": "2026-06-30T22:46:37Z", "completed_at": "2026-06-30T23:01:09Z", "related_alerts_count": 0, "tags": ["exfil"] } ], "total": 1, "limit": 25, "offset": 0 } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 400 | invalid_request | Unknown query parameter or invalid enum/range. | | 502 | upstream_error | Upstream engine returned a failure. | | 503 | service_unavailable | No engine is available for the tenant. | | 504 | upstream_timeout | Upstream request timed out (30s). | #### SDK notes - prophet.investigations.iter(*, disposition=None, min_confidence=None, since=None, until=None, sort=None, page_size=100, start_offset=0) streams InvestigationListItem across all pages (auto-pagination; page_size clamped to 1..200). - The SDK speaks the external vocabulary — pass disposition="escalate"; the mapping to internal values happens server-side. ### GET /rest/investigations/1.0/:id — Get investigation Docs URL: https://docs.prophet.io/#api-investigations-get Family: Investigations Auth: bearer token Scope: p.token.scope.investigations_api Version: 1.0 Stability: stable Returns one full Apollo investigation — verdict, key findings, provenance lineage, and recommended actions. Available once analysis completes. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | id | string | yes | Investigation id from the list endpoint. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | id | string | n/a | Investigation id. | | response | status | "running" \| "completed" \| "failed" | n/a | Lifecycle state (completed for any record returned). | | response | created_at | string \| null | n/a | When Apollo opened the investigation. | | response | completed_at | string \| null | n/a | When Apollo closed it. | | response | trigger.source | string \| null | n/a | Originating entity of the flagged activity. | | response | trigger.destination | string \| null | n/a | Counterparty of the flagged activity. | | response | trigger.detected_at | string \| null | n/a | When the triggering activity was flagged. | | response | trigger.signal.volume_bytes | number \| null | n/a | Bytes transferred on the flagged activity. | | response | trigger.signal.anomaly_score | number \| null | n/a | Overall detector score for the flag. | | response | trigger.signal.surprise | number \| null | n/a | How far the observed volume departs from what this source produces on its own. | | response | trigger.signal.mismatch | number \| null | n/a | Behavioral-mismatch magnitude (0..10). | | response | verdict | object \| null | n/a | Apollo's conclusion. null while running. | | response | verdict.disposition | "benign" \| "malicious" \| "escalate" | n/a | Apollo's call. | | response | verdict.confidence | number | n/a | Confidence 0..1 — calibrated on evidence held, not ideal evidence. | | response | verdict.headline | string | n/a | One- to two-sentence plain-language answer. | | response | verdict.rationale | string | n/a | Fuller reasoning, 1–3 paragraphs. | | response | at_a_glance | object \| null | n/a | Known / unknown / therefore syllogism. null while running. | | response | at_a_glance.known | string | n/a | The proven core, one sentence. | | response | at_a_glance.unknown | string \| null | n/a | The single decisive unresolved fact. null when the verdict is clean. | | response | at_a_glance.therefore | string | n/a | The call plus the single most important next move. | | response | key_findings[] | object[] | n/a | The observations that drove the verdict. | | response | key_findings[].headline | string | n/a | The scannable takeaway. | | response | key_findings[].observation | string | n/a | What was observed — specific and traceable. | | response | key_findings[].significance | string | n/a | Why it matters. | | response | key_findings[].role | "decisive" \| "supporting" \| "ambiguous" \| "context" \| "data_gap" | n/a | Epistemic role of the finding. | | response | key_findings[].importance | "high" \| "medium" \| "low" | n/a | Relative weight of the finding. | | response | key_findings[].rules_out | string[] | n/a | Candidate explanations this finding rules out (resolved names). | | response | key_findings[].confirms | string[] | n/a | Candidate explanations this finding confirms (resolved names). | | response | key_findings[].timeline[] | object[] \| null | n/a | Temporal anchors: { at, label, children? }. | | response | key_findings[].traffic_links[] | object[] \| null | n/a | Replayable pointers into the underlying flows. | | response | key_findings[].traffic_links[].label | string | n/a | Chip text, e.g. "April 9 upload flows (78 flows)". | | response | key_findings[].traffic_links[].rationale | string \| null | n/a | Why this query demonstrates the finding. | | response | key_findings[].traffic_links[].query | object \| null | n/a | The internal search query (tenant fields stripped). Opaque/unstable in v1. | | response | provenance | object \| null | n/a | Access→exfiltration lineage. null when the source host collects no host-logs. | | response | provenance.available | boolean | n/a | Whether a chain was reconstructed. When false, render the ceiling not an empty chain. | | response | provenance.unavailable_reason | string \| null | n/a | Why no chain (e.g. host collects no host-logs). | | response | provenance.headline | string \| null | n/a | One-line narrative of the whole chain. | | response | provenance.host | string \| null | n/a | Resolved source hostname. | | response | provenance.completeness | number | n/a | Fraction of legs directly observed (0..1). | | response | provenance.host_value | object \| null | n/a | What the host is worth to an attacker: { role, value, reach }. | | response | provenance.host_value.value | "low" \| "moderate" \| "high" | n/a | Attacker value of the host. | | response | provenance.legs[] | object[] | n/a | Ordered kill-chain steps. | | response | provenance.legs[].stage | "access" \| "identity" \| "execution" \| "collection" \| "exfiltration" \| "lateral" | n/a | Kill-chain tactic bucket. | | response | provenance.legs[].title | string | n/a | Display heading, e.g. "Exfiltration". | | response | provenance.legs[].headline | string | n/a | One-line leg takeaway. | | response | provenance.legs[].detail | string \| null | n/a | Longer detail for the leg. | | response | provenance.legs[].confidence | "directly_observed" \| "inferred" \| "not_established" | n/a | Evidentiary confidence for the leg. | | response | provenance.legs[].gap_reason | string \| null | n/a | Why the link is not directly observed (when confidence ≠ directly_observed). | | response | provenance.legs[].at | string \| null | n/a | When (ISO-8601 or window string). | | response | provenance.legs[].actors[] | object[] | n/a | The who/what on this leg: { kind, label, detail? }. | | response | provenance.legs[].actors[].kind | "human" \| "account" \| "process" \| "source_ip" \| "tool" \| "path" \| "destination" | n/a | Actor type. | | response | provenance.legs[].attack[] | object[] | n/a | MITRE ATT&CK techniques: { tactic, technique_id, technique }. | | response | provenance.legs[].pivot_keys | string[] | n/a | Join keys linking this leg to adjacent ones, e.g. ["uid=1002"]. | | response | decision_support.confidence_limits | string \| null | n/a | The honest cap on certainty given available telemetry. | | response | decision_support.what_would_change_the_verdict | string \| null | n/a | The single artifact that would flip the verdict. | | response | decision_support.open_questions[] | object[] | n/a | Unresolved questions: { question, needed_data?, priority? }. | | response | decision_support.open_questions[].priority | "highest" \| "high" \| "medium" \| "low" | n/a | Priority of the question. | | response | decision_support.recommended_actions[] | object[] | n/a | Next steps: { timeframe, action }. | | response | meta.generated_at | string \| null | n/a | When this analysis was produced. | | response | meta.ai_generated | true | n/a | Always true — the analysis is AI-authored. | | response | meta.analysis_version | string \| null | n/a | Opaque version of the analysis format. | #### Python SDK Method: `prophet.investigations.get(investigation_id: str) -> Investigation | None` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | investigation_id | str | yes | Investigation id. The SDK raises ValidationError when empty. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | Investigation \| None | n/a | None when the API responds 404 (missing, not yours, or still running). | | response | Investigation.verdict | Verdict \| None | n/a | disposition, confidence, headline, rationale. | | response | Investigation.key_findings | list[KeyFinding] | n/a | headline, observation, significance, role, rules_out, confirms, traffic_links. | | response | Investigation.provenance | Provenance \| None | n/a | Access→exfiltration legs with actors + MITRE attack refs. | | response | Investigation.decision_support | DecisionSupport | n/a | confidence_limits, what_would_change_the_verdict, open_questions, recommended_actions. | | response | Investigation.is_malicious / .needs_escalation / .has_provenance | bool | n/a | Convenience checks. | SDK example: ```python inv = prophet.investigations.get("inv_43d67a...") if inv is None: print("not found or not yet complete") elif inv.needs_escalation: print(inv.at_a_glance.therefore) for f in inv.key_findings: print(f.role, f.headline, "— rules out:", f.rules_out) if inv.has_provenance: for leg in inv.provenance.legs: print(leg.stage, leg.headline, leg.confidence) ``` REST example: ```bash curl https://app.prophet.io/rest/investigations/1.0/inv_43d67a... \ -H "Authorization: Bearer $PROPHET_TOKEN" ``` Example response: ```json { "id": "inv_43d67a...", "status": "completed", "trigger": { "source": "10.90.8.16", "destination": "google", "detected_at": "2026-06-29T20:00:00Z", "signal": { "volume_bytes": 616433024, "anomaly_score": 1, "surprise": 2.74, "mismatch": 4.7 } }, "verdict": { "disposition": "escalate", "confidence": 0.85, "headline": "A first-time 616 MB export to Google Drive under valid credentials; intent unresolvable from telemetry.", "rationale": "..." }, "at_a_glance": { "known": "prophetadmin staged flows.ndjson and pushed ~616 MB to Google Drive — a first-ever export, no escalation or persistence.", "unknown": "Whether the export was authorized — no change-management record exists to confirm it.", "therefore": "Escalate for a same-day human authorization check." }, "key_findings": [ { "headline": "First-ever rclone-to-Drive export by this user", "observation": "Zero prior rclone transfers to gdrive across 179 days; the only copyto is the flagged event.", "significance": "No routine precedent, so it cannot be explained as a recurring sync.", "role": "decisive", "importance": "high", "rules_out": ["Legitimate recurring routine"], "confirms": [], "traffic_links": [ { "label": "Daily outbound bytes to Google (179 days)", "rationale": "Flat baseline with one spike.", "query": { "query": "src.ip eq 10.90.8.16 and dst.ctx.organization eq google", "config": { "module": "timeseries" } } } ] } ], "provenance": { "available": true, "host": "marketing1", "completeness": 0.8, "legs": [ { "stage": "exfiltration", "headline": "rclone pushed ~616 MB to Google Drive over HTTPS.", "confidence": "directly_observed", "actors": [{ "kind": "process", "label": "rclone (pid 21210)" }], "attack": [{ "tactic": "Exfiltration", "technique_id": "T1567.002", "technique": "Exfil to Cloud Storage" }] } ] }, "decision_support": { "confidence_limits": "Network telemetry cannot see file contents or a backup manifest.", "what_would_change_the_verdict": "A Workspace admin-log entry tying this to an approved backup would flip it to benign.", "open_questions": [{ "question": "Is there an approved backup job?", "needed_data": "Google Workspace Admin Audit logs", "priority": "high" }], "recommended_actions": [{ "timeframe": "within_4h", "action": "Confirm with the host owner whether this was sanctioned." }] }, "meta": { "generated_at": "2026-06-30T23:01:09Z", "ai_generated": true, "analysis_version": "v2" } } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 404 | not_found | Investigation does not exist, is not yours, has not finished yet, or the upstream lookup failed. | | 503 | service_unavailable | No engine is available for the tenant. | | 504 | upstream_timeout | Upstream request timed out (30s). | #### SDK notes - A running investigation has no summary yet — get() returns None until it completes. - traffic_links[].query is the internal search query passed through with tenant fields stripped; opaque/unstable in v1. ## Explore External-organization communication shape: which external services a network sends traffic to, and the texture of each relationship (when, rhythm, transfer, who, how). Communication shape is what Prophet models to detect breaches — these endpoints expose the same view of your network for exploration. ### GET /rest/explore/1.0/egress/organizations — List external organizations Docs URL: https://docs.prophet.io/#api-explore-egress-organizations Family: Explore Auth: bearer token Scope: p.token.scope.explore_api Version: 1.0 Stability: stable External organizations the network sent traffic to, ranked by volume and merged across instance_ids. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | instance_ids | string[] | yes | Instances/customer IDs to aggregate over (merged). Self or authorized children. | | body | start | DateTimeFilter | no | Start time filter. Defaults to relative 24 hours. | | body | end | DateTimeFilter | no | End time filter. Defaults to now. | | body | size | number | no | Maximum organizations to return. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | organizations[].name | string | n/a | Organization name (dst.ctx.organization). | | response | organizations[].industry | string \| null | n/a | Classified industry for the organization. | | response | organizations[].apps[] | string[] | n/a | Up to three top application labels by flow count. | | response | organizations[].bytes | number | n/a | Total bytes (upload + download). | | response | organizations[].upload | number | n/a | Egress bytes (network -> org). | | response | organizations[].download | number | n/a | Ingress bytes (org -> network). | | response | organizations[].flows | number | n/a | Flow records in the window. | | response | organizations[].sources | number | n/a | Distinct internal hosts talking to the org. | | response | total_orgs | number | n/a | Distinct external organizations in the window. | | response | coverage.classified_in_page | number | n/a | Flows behind the returned orgs. Org classification covers a minority of flows. | | response | coverage.total_flows | number | n/a | Total flows in the window. | | response | base_pql | string | n/a | PQL fragment scoping the egress view; combine it with your own predicates in the Search API. | | response | results_for[] | string[] | n/a | Instance IDs the merged data covers. | | response | status | string | n/a | success on 200; failure with ui_message when the query fails. | #### Python SDK Method: `prophet.explore.egress.organizations(instances: str | list[str], *, start: TimeFilter | None = None, end: TimeFilter | None = None, size: int = 25) -> OrganizationList` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | instances | str \| list[str] | yes | A customer ID or list of IDs. Sent as instance_ids. | | response | start | TimeFilter \| None | no | Now, MinutesAgo, HoursAgo, DaysAgo, WeeksAgo, or At. | | response | end | TimeFilter \| None | no | End time filter. | | response | size | int | no | Max organizations. Default 25. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | OrganizationList | n/a | Typed result: .organizations, .total_orgs, .coverage, .results_for. | | response | OrganizationRow | model | n/a | name, bytes, upload, download, flows, sources. | SDK example: ```python from prophet.sdk import HoursAgo, Now orgs = prophet.explore.egress.organizations("acme_msp", start=HoursAgo(24)) for o in orgs.organizations: print(o.name, o.upload, o.download, o.sources) ``` REST example: ```bash curl -X GET https://app.prophet.io/rest/explore/1.0/egress/organizations \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "instance_ids": ["acme_msp-d7890e123"], "start": { "relative": { "value": 24, "unit": "hours" } }, "end": { "now": true }, "size": 25 }' ``` Example response: ```json { "organizations": [ { "name": "mongodb", "industry": "technology", "apps": ["MongoDB"], "bytes": 131448400959, "upload": 115369237055, "download": 15979163904, "flows": 1045864, "sources": 994 } ], "total_orgs": 45, "coverage": { "classified_in_page": 1217928, "total_flows": 73961652 }, "base_pql": "src.address_type eq private and dst.address_type eq public and dst.ctx.organization ex", "status": "success", "results_for": ["acme_msp-d7890e123"] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | #### SDK notes - Results are merged across instances; results_for echoes the covered scope. ### GET /rest/explore/1.0/egress/organizations/:org — Organization header Docs URL: https://docs.prophet.io/#api-explore-egress-organization Family: Explore Auth: bearer token Scope: p.token.scope.explore_api Version: 1.0 Stability: stable Stable header attributes for one organization: geo, industry, host and endpoint counts, processes, and a plain-language readout. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | org | string | yes | Organization name (dst.ctx.organization). | | body | instance_ids | string[] | yes | Instances/customer IDs to aggregate over. | | body | start | DateTimeFilter | no | Start time filter. Defaults to relative 24 hours. | | body | end | DateTimeFilter | no | End time filter. Defaults to now. | | body | src_ip | string | no | Scope to one internal host -> org relationship. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | org | string | n/a | Organization name. | | response | industry | string \| null | n/a | Classified industry. | | response | geo | string \| null | n/a | Top destination country. | | response | upload / download | number | n/a | Egress / ingress bytes. | | response | sources / endpoints | number | n/a | Distinct internal hosts / org endpoints. | | response | processes[] | string[] | n/a | Top internal processes reaching the org (packet-sourced). | | response | readout | string | n/a | Plain-language summary. | | response | results_for[] | string[] | n/a | Instance IDs covered. | #### Python SDK Method: `prophet.explore.egress.organization(instances: str | list[str], org: str, *, start: TimeFilter | None = None, end: TimeFilter | None = None, src_ip: str | None = None) -> OrganizationHeader` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | instances | str \| list[str] | yes | Customer ID or list. | | response | org | str | yes | Organization name. | | response | src_ip | str \| None | no | Scope to one host. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | OrganizationHeader | n/a | org, industry, geo, upload, download, sources, endpoints, processes, readout. | SDK example: ```python header = prophet.explore.egress.organization("acme_msp", "google") print(header.geo, header.industry, header.processes, header.readout) ``` REST example: ```bash curl -X GET https://app.prophet.io/rest/explore/1.0/egress/organizations/google \ -H "Authorization: Bearer $PROPHET_TOKEN" -H "Content-Type: application/json" \ -d '{"instance_ids":["acme_msp-d7890e123"]}' ``` Example response: ```json { "org": "google", "industry": "technology", "geo": "United States", "upload": 32284038, "download": 632326453, "sources": 88, "endpoints": 1, "processes": ["python", "python3"], "readout": "88 internal host(s) -> 1 endpoint(s); driven by python", "status": "success", "results_for": ["acme_msp-d7890e123"] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | ### GET /rest/explore/1.0/egress/organizations/:org/temporal — Organization — temporal Docs URL: https://docs.prophet.io/#api-explore-egress-temporal Family: Explore Auth: bearer token Scope: p.token.scope.explore_api Version: 1.0 Stability: stable WHEN: a day-of-week by hour-of-day heatmap of traffic to the organization. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | org | string | yes | Organization name. | | body | instance_ids | string[] | yes | Instances to aggregate over. | | body | start | DateTimeFilter | no | Start time. Defaults to relative 7 days. | | body | end | DateTimeFilter | no | End time. Defaults to now. | | body | src_ip | string | no | Scope to one host. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | cells[].row | string | n/a | Day-of-week label (Mon..Sun). | | response | cells[].col | string | n/a | Hour-of-day label (00..23). | | response | cells[].value | number | n/a | Flow count for that day/hour cell. | | response | rows | string[] | n/a | Ordered day labels (Mon..Sun). | | response | cols | string[] | n/a | Ordered hour labels (00..23). | | response | total | number | n/a | Total flows in the window. | | response | readout | string | n/a | e.g. "always-on — traffic in nearly every hour (automated)". | #### Python SDK Method: `prophet.explore.egress.temporal(instances, org, *, start=None, end=None, src_ip=None) -> Temporal` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | instances | str \| list[str] | yes | Customer ID or list. | | response | org | str | yes | Organization name. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | Temporal | n/a | .cells, .rows, .cols, .total, .readout. | SDK example: ```python t = prophet.explore.egress.temporal("acme_msp", "google", start=HoursAgo(168)) print(t.total, t.readout) ``` REST example: ```bash curl -X GET https://app.prophet.io/rest/explore/1.0/egress/organizations/google/temporal \ -H "Authorization: Bearer $PROPHET_TOKEN" -H "Content-Type: application/json" \ -d '{"instance_ids":["acme_msp-d7890e123"],"start":{"relative":{"value":168,"unit":"hours"}},"end":{"now":true}}' ``` Example response: ```json { "cells": [{ "row": "Mon", "col": "09", "value": 240 }], "rows": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], "cols": ["00", "01", "02"], "total": 55286, "readout": "always-on — traffic in nearly every hour (automated)", "status": "success", "results_for": ["acme_msp-d7890e123"] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | ### GET /rest/explore/1.0/egress/organizations/:org/cadence — Organization — cadence Docs URL: https://docs.prophet.io/#api-explore-egress-cadence Family: Explore Auth: bearer token Scope: p.token.scope.explore_api Version: 1.0 Stability: stable RHYTHM: is the communication consistent or random? The session-gap CDF (stepped = machine-driven) plus the beacon-flagged subset. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | org | string | yes | Organization name. | | body | instance_ids | string[] | yes | Instances to aggregate over. | | body | start | DateTimeFilter | no | Start time. Defaults to relative 24 hours. | | body | end | DateTimeFilter | no | End time. Defaults to now. | | body | src_ip | string | no | Scope to one internal host -> org relationship. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | cdf[].gap_secs | number | n/a | Session-gap value at this point of the CDF, in seconds. | | response | cdf[].fraction | number | n/a | Fraction of sessions with a gap at or below gap_secs (0..1). | | response | dominant_intervals[] | { gap_secs, fraction }[] | n/a | Up to three dominant beats — vertical steps of the CDF holding at least 15% of the mass. A strong beat means a scheduler owns the relationship. | | response | steppedness | number \| null | n/a | Mass fraction of the largest beat (~1.0 means one interval dominates: machine-driven). | | response | median_gap_secs | number \| null | n/a | Median session gap in seconds. | | response | stats | object | n/a | Map of metric name -> value: mean regularity, mean_gap (seconds), and burstiness. | | response | beacon.flows | number | n/a | Beacon-flagged (periodic) flow count. | | response | beacon.sources | number | n/a | Distinct sources with beacon-flagged flows. | | response | beacon.interval_ms | number \| null | n/a | Mean detected beacon interval in milliseconds. | | response | readout | string | n/a | e.g. "steady rhythm, ~60 s between flows". | #### Python SDK Method: `prophet.explore.egress.cadence(instances, org, *, start=None, end=None, src_ip=None) -> Cadence` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | instances | str \| list[str] | yes | Customer ID or list. | | response | org | str | yes | Organization name. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | Cadence | n/a | .cdf, .dominant_intervals, .steppedness, .median_gap_secs, .stats, .beacon, .readout. | SDK example: ```python c = prophet.explore.egress.cadence("acme_msp", "google") print(c.beacon, c.readout) ``` REST example: ```bash curl -X GET https://app.prophet.io/rest/explore/1.0/egress/organizations/google/cadence \ -H "Authorization: Bearer $PROPHET_TOKEN" -H "Content-Type: application/json" \ -d '{"instance_ids":["acme_msp-d7890e123"]}' ``` Example response: ```json { "cdf": [{ "gap_secs": 0.62, "fraction": 0.05 }, { "gap_secs": 26.5, "fraction": 0.5 }, { "gap_secs": 61.2, "fraction": 0.95 }], "dominant_intervals": [{ "gap_secs": 60.0, "fraction": 0.42 }], "steppedness": 0.42, "median_gap_secs": 26.5, "stats": { "stats.timing.inter_arrival_secs.regularity": 10.2, "stats.timing.inter_arrival_secs.mean_gap": 26.5 }, "beacon": { "flows": 24, "sources": 4, "interval_ms": 19.9 }, "readout": "bursty / irregular, ~26.5 s between flows, 24 beacon-like flows from 4 sources", "status": "success", "results_for": ["acme_msp-d7890e123"] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | ### GET /rest/explore/1.0/egress/organizations/:org/transfer — Organization — transfer Docs URL: https://docs.prophet.io/#api-explore-egress-transfer Family: Explore Auth: bearer token Scope: p.token.scope.explore_api Version: 1.0 Stability: stable WHAT IS MOVED: chunk size (mean and coefficient of variation), payload entropy, session duration, and upload/download split. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | org | string | yes | Organization name. | | body | instance_ids | string[] | yes | Instances to aggregate over. | | body | start | DateTimeFilter | no | Start time. Defaults to relative 24 hours. | | body | end | DateTimeFilter | no | End time. Defaults to now. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | transfer.chunk_bytes_mean | number | n/a | Mean packet/chunk size (packet-sourced). | | response | transfer.chunk_cv | number | n/a | Coefficient of variation — low is uniform (bulk sync), high is mixed. | | response | transfer.entropy_mean | number \| null | n/a | Mean payload entropy (encrypted/compressed when high). | | response | transfer.duration_mean | number \| null | n/a | Mean session duration in seconds. | | response | transfer.up / transfer.down | number | n/a | Egress / ingress bytes. | | response | readout | string | n/a | e.g. "~64 KB uniform chunks, encrypted, upload-heavy". | #### Python SDK Method: `prophet.explore.egress.transfer(instances, org, *, start=None, end=None, src_ip=None) -> Transfer` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | instances | str \| list[str] | yes | Customer ID or list. | | response | org | str | yes | Organization name. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | Transfer | n/a | .transfer (chunk_bytes_mean, chunk_cv, entropy_mean, duration_mean, up, down), .readout. | SDK example: ```python x = prophet.explore.egress.transfer("acme_msp", "mongodb") print(x.transfer.chunk_bytes_mean, x.transfer.chunk_cv, x.readout) ``` REST example: ```bash curl -X GET https://app.prophet.io/rest/explore/1.0/egress/organizations/mongodb/transfer \ -H "Authorization: Bearer $PROPHET_TOKEN" -H "Content-Type: application/json" \ -d '{"instance_ids":["acme_msp-d7890e123"]}' ``` Example response: ```json { "transfer": { "chunk_bytes_mean": 681.3, "chunk_cv": 0.80, "entropy_mean": 6.84, "duration_mean": 11.3, "up": 114882003430, "down": 16013469796 }, "readout": "~681 B mixed-size chunks, 11.3 s sessions, encrypted/compressed payloads, upload-heavy", "status": "success", "results_for": ["acme_msp-d7890e123"] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | #### SDK notes - chunk_bytes_mean / chunk_cv describe packet-level chunking; entropy is measured on packet payloads. ### GET /rest/explore/1.0/egress/organizations/:org/reach — Organization — reach Docs URL: https://docs.prophet.io/#api-explore-egress-reach Family: Explore Auth: bearer token Scope: p.token.scope.explore_api Version: 1.0 Stability: stable WHO / WHAT: the internal sources and processes reaching the organization. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | org | string | yes | Organization name. | | body | instance_ids | string[] | yes | Instances to aggregate over. | | body | start | DateTimeFilter | no | Start time. Defaults to relative 24 hours. | | body | end | DateTimeFilter | no | End time. Defaults to now. | | body | size | number | no | Maximum sources to return. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | sources[].ip | string | n/a | Internal host IP. | | response | sources[].upload | number | n/a | Egress bytes from this host to the org. | | response | sources[].download | number | n/a | Ingress bytes from the org to this host. | | response | sources[].flows | number | n/a | Flow records for this host. | | response | processes[].name | string | n/a | Process name (packet-sourced eBPF attribution). | | response | processes[].flows | number | n/a | Flow records attributed to the process. | | response | processes[].sources | number | n/a | Distinct internal hosts running the process. | | response | source_count | number | n/a | Distinct internal hosts. | | response | readout | string | n/a | e.g. "26 internal host(s) -> 25 endpoint(s); driven by jumpcloud-agent". | #### Python SDK Method: `prophet.explore.egress.reach(instances, org, *, start=None, end=None, src_ip=None, size=50) -> Reach` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | instances | str \| list[str] | yes | Customer ID or list. | | response | org | str | yes | Organization name. | | response | size | int | no | Max sources. Default 50. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | Reach | n/a | .sources, .processes, .source_count, .readout. | SDK example: ```python r = prophet.explore.egress.reach("acme_msp", "jumpcloud") for s in r.sources: print(s.ip, s.upload, s.flows) print([p.name for p in r.processes]) ``` REST example: ```bash curl -X GET https://app.prophet.io/rest/explore/1.0/egress/organizations/jumpcloud/reach \ -H "Authorization: Bearer $PROPHET_TOKEN" -H "Content-Type: application/json" \ -d '{"instance_ids":["acme_msp-d7890e123"]}' ``` Example response: ```json { "sources": [{ "ip": "10.90.77.1", "upload": 2852885, "download": 2013571, "flows": 622 }], "processes": [{ "name": "jumpcloud-agent", "flows": 4030, "sources": 26 }], "source_count": 26, "readout": "26 internal host(s) -> 26 endpoint(s); driven by jumpcloud-agent", "status": "success", "results_for": ["acme_msp-d7890e123"] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | #### SDK notes - Add src_ip to any dimension to scope the whole fingerprint to one host -> org relationship. ### GET /rest/explore/1.0/egress/organizations/:org/access — Organization — access Docs URL: https://docs.prophet.io/#api-explore-egress-access Family: Explore Auth: bearer token Scope: p.token.scope.explore_api Version: 1.0 Stability: stable HOW REACHED: TLS versions, ports, protocols, and applications used to reach the organization. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | org | string | yes | Organization name. | | body | instance_ids | string[] | yes | Instances to aggregate over. | | body | start | DateTimeFilter | no | Start time. Defaults to relative 24 hours. | | body | end | DateTimeFilter | no | End time. Defaults to now. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | tls_versions[].name | string | n/a | TLS version label (e.g. TLS 1.3). | | response | tls_versions[].flows | number | n/a | Flow records at this TLS version. | | response | tls_versions[].bytes | number | n/a | Total bytes at this TLS version. | | response | ports[] | { name, flows, bytes }[] | n/a | Destination ports. Same row shape as tls_versions[]. | | response | protocols[] | { name, flows, bytes }[] | n/a | Transport protocols. Same row shape as tls_versions[]. | | response | apps[] | { name, flows, bytes }[] | n/a | Application labels from protocol classification. Same row shape as tls_versions[]. | | response | readout | string | n/a | e.g. "TLS 1.3 / port 443 / HTTPS.Google". | #### Python SDK Method: `prophet.explore.egress.access(instances, org, *, start=None, end=None, src_ip=None) -> Access` | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | instances | str \| list[str] | yes | Customer ID or list. | | response | org | str | yes | Organization name. | SDK outputs: | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | return | Access | n/a | .tls_versions, .ports, .protocols, .apps, .readout. | SDK example: ```python a = prophet.explore.egress.access("acme_msp", "google") print([(v.name, v.flows) for v in a.tls_versions], a.readout) ``` REST example: ```bash curl -X GET https://app.prophet.io/rest/explore/1.0/egress/organizations/google/access \ -H "Authorization: Bearer $PROPHET_TOKEN" -H "Content-Type: application/json" \ -d '{"instance_ids":["acme_msp-d7890e123"]}' ``` Example response: ```json { "tls_versions": [{ "name": "TLS 1.3", "flows": 1163, "bytes": 9600000 }, { "name": "TLS 1.0", "flows": 114 }], "ports": [{ "name": "443", "flows": 6607 }], "protocols": [{ "name": "TCP", "flows": 6607 }], "apps": [{ "name": "HTTPS.Google", "flows": 6313 }], "readout": "TLS 1.3 / port 443 / HTTPS.Google", "status": "success", "results_for": ["acme_msp-d7890e123"] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | ## Events Fetch event topics and records from Prophet plugin activity. ### GET /rest/events/1.0/:customerId — Fetch event topics Docs URL: https://docs.prophet.io/#api-events-topics Family: Events Auth: bearer token Scope: p.token.scope.event_api Version: 1.0 Stability: stable Fetches plugin event topics for a customer. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | customerId | string | yes | Tenant/customer ID whose event topics should be fetched. | | body | start | DateFilter | no | Start date filter. Defaults to relative 15 minutes. | | body | end | DateFilter | no | End date filter. Defaults to now. | | body | modules | string[] | no | Module filter. Omit to apply no module filter. | | body | states | string[] | no | Event state filter. | | body | plugins | string[] | no | Plugin name filter. | | body | labels | string[] | no | Label filter applied to event records. | | body | sort | { field: string, order: "asc" \| "desc" }[] | no | Sort entries applied to the result set. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | operation | "fetch_topics" | n/a | Operation echoed in the response. | | response | status | string | n/a | Request status, usually success. | | response | events[] | object[] | n/a | Event topic summaries returned as notifications. | | response | returned | number | n/a | Number of topics returned. | REST example: ```bash curl -X GET https://app.prophet.io/rest/events/1.0/acme_msp-d7890e123 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "start": { "relative": { "value": 24, "unit": "hours" } }, "end": { "now": true }, "modules": ["infrastructure"], "states": ["active"], "sort": [{ "field": "@timestamp", "order": "desc" }] }' ``` Example response: ```json { "operation": "fetch_topics", "status": "success", "events": [ { "id": "evt-123", "module": "infrastructure", "plugin": "zeek", "state": "active", "labels": ["prod"] } ], "returned": 1 } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 403 | cross_instance_authorization | :customerId is outside the caller tenant tree. | | 500 | compute_engine_error | Upstream request failed. | ### GET /rest/events/1.0/:customerId/:eventId — Fetch event records Docs URL: https://docs.prophet.io/#api-events-records Family: Events Auth: bearer token Scope: p.token.scope.event_api Version: 1.0 Stability: stable Fetches event records for one event topic. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | path | customerId | string | yes | Tenant/customer ID whose records should be fetched. | | path | eventId | string | yes | Event topic identifier. | | body | start | DateFilter | no | Start date filter. Defaults to relative 15 minutes. | | body | end | DateFilter | no | End date filter. Defaults to now. | | body | sort | { field: string, order: "asc" \| "desc" }[] | no | Sort entries applied to the result set. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | page_size | number \| null | n/a | Page size from searchProps when returned by the engine. | | response | page_next | string \| number \| null | n/a | Next page cursor from searchProps when returned by the engine. | | response | returned | number | n/a | Number of event records returned. | | response | operation | "fetch_records" | n/a | Operation echoed in the response. | | response | status | string | n/a | Request status, usually success. | | response | parts[] | object[] | n/a | Event record rows for the topic. | REST example: ```bash curl -X GET https://app.prophet.io/rest/events/1.0/acme_msp-d7890e123/evt-123 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "start": { "relative": { "value": 24, "unit": "hours" } }, "end": { "now": true }, "sort": [{ "field": "@timestamp", "order": "desc" }] }' ``` Example response: ```json { "page_size": 100, "page_next": "cursor-2", "returned": 1, "operation": "fetch_records", "status": "success", "parts": [ { "id": "part-123", "event_id": "evt-123", "@timestamp": "2026-02-04T16:00:00.000Z" } ] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 403 | cross_instance_authorization | :customerId is outside the caller tenant tree. | | 500 | compute_engine_error | Upstream request failed. | ## Automation List, set, and delete automations scoped to a tenant or child tenant. ### GET /rest/automation/1.0 — List or fetch automations Docs URL: https://docs.prophet.io/#api-automation-list Family: Automation Auth: bearer token Scope: p.token.scope.automation_api Version: 1.0 Stability: stable Lists automations or fetches one automation when both id and feed_id are supplied. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | instance_id | string | yes | Target tenant/customer ID. | | body | filter | string | no | Text filter forwarded as textFilter to the automation engine. | | body | id | string | no | Automation ID. Include feed_id with id to fetch one automation. | | body | feed_id | string | no | Automation feed. Defaults to local; required with id when fetching one automation. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | "success" \| "error" \| string | n/a | Automation engine status. | | response | operation | "list" \| "fetch" | n/a | list when id is omitted, fetch when id and feed_id are supplied. | | response | automations[] | object[] \| omitted | n/a | Declarative automations for list responses. | | response | automation | object \| omitted | n/a | Declarative automation for fetch responses. | | response | message | string \| omitted | n/a | Error or informational message when returned by the engine. | REST example: ```bash curl -X GET https://app.prophet.io/rest/automation/1.0 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "instance_id": "acme_msp-d7890e123", "filter": "dns" }' ``` Example response: ```json { "status": "success", "operation": "list", "automations": [ { "id": "auto-123", "feed_id": "local", "name": "DNS exfiltration guard", "tags": ["dns"], "enabled": true } ] } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 400 | body.validation | id and feed_id must be supplied together when fetching one automation. | | 403 | cross_instance_authorization | instance_id is missing or outside the caller tenant tree. | ### POST /rest/automation/1.0 — Create or update automation Docs URL: https://docs.prophet.io/#api-automation-set Family: Automation Auth: bearer token Scope: p.token.scope.automation_api Version: 1.0 Stability: stable Creates or updates an automation pipeline for a tenant. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | instance_id | string | yes | Target tenant/customer ID. | | body | name | string | yes | Automation display name. | | body | pipeline | object[] \| object | yes | Declarative automation pipeline forwarded to the automation engine. | | body | tags | string[] | no | Tags stored with the automation. Defaults to an empty list. | | body | feed_id | string | no | Automation feed. Defaults to local. | | body | id | string | no | Automation ID. Defaults to a generated UUID for creates. | | body | enabled | boolean | no | Enabled state. Controller coerces with Boolean(enabled). | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | "success" \| "error" \| string | n/a | Automation engine status. | | response | operation | "set" | n/a | Create/update operation. | | response | automation | object | n/a | Saved declarative automation returned by the automation engine. | | response | message | string \| omitted | n/a | Error message when the engine reports status error. | | response | customer_id | string \| omitted | n/a | Customer ID included by the engine on errors. | REST example: ```bash curl -X POST https://app.prophet.io/rest/automation/1.0 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "instance_id": "acme_msp-d7890e123", "name": "DNS exfiltration guard", "pipeline": [{ "type": "match", "field": "dst.port", "operator": "eq", "value": 53 }], "tags": ["dns"], "enabled": true }' ``` Example response: ```json { "status": "success", "operation": "set", "automation": { "id": "auto-123", "feed_id": "local", "name": "DNS exfiltration guard", "tags": ["dns"], "enabled": true } } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 403 | cross_instance_authorization | instance_id is missing or outside the caller tenant tree. | ### DELETE /rest/automation/1.0 — Delete automation Docs URL: https://docs.prophet.io/#api-automation-delete Family: Automation Auth: bearer token Scope: p.token.scope.automation_api Version: 1.0 Stability: stable Deletes an automation by ID for the scoped instance. #### API inputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | body | instance_id | string | yes | Target tenant/customer ID. | | body | id | string | yes | Automation ID to delete. | #### API outputs | Location | Name | Type | Required | Description | | --- | --- | --- | --- | --- | | response | status | "success" \| "info" \| "error" \| string | n/a | success when deleted, info when the automation does not exist, or error from the engine. | | response | operation | "delete" | n/a | Delete operation. | | response | message | string \| omitted | n/a | Informational or error message returned by the engine. | | response | customer_id | string \| omitted | n/a | Customer ID included by the engine on errors. | REST example: ```bash curl -X DELETE https://app.prophet.io/rest/automation/1.0 \ -H "Authorization: Bearer $PROPHET_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "instance_id": "acme_msp-d7890e123", "id": "auto-123" }' ``` Example response: ```json { "status": "success", "operation": "delete" } ``` #### Errors | Status | Code | Description | | --- | --- | --- | | 401 | authentication_error | Missing, expired, or invalid bearer token. | | 403 | authorization_error | Token lacks the required scope or tenant access. | | 403 | cross_instance_authorization | instance_id is missing or outside the caller tenant tree. |