When you run Neo through the API, you usually want the results flowing straight into your own systems: a security data lake, a ticketing system, a SIEM, or your own remediation pipeline. Instead of polling for status and copying results by hand, Neo can hand off a structured completion artifact the moment a task finishes.
There are two ways to receive that artifact:
| Delivery mode | What Neo does on completion | How you consume it |
|---|
| Webhook (push) | POSTs the completion artifact to your configured team webhook URL | Your endpoint receives a task.completed payload in real time |
| Artifact (pull) | Persists the completion artifact for later retrieval | You fetch it with GET /api/v1/tasks/{id}/artifacts |
Both modes return the same artifact payload. You choose the mode per task with the completion_delivery parameter when you create the task. When you omit it, no completion handoff runs and results stay in Neo.
This capability is aimed at teams integrating Neo into external systems. It is configured through the API and team settings.
What’s in the completion artifact
The completion artifact is a single structured snapshot of a finished task — everything a downstream system needs to act on the result without scraping the transcript. At a high level it captures:
- Outcome — the final task status and when the task completed.
- Summary — a human-readable description of what Neo did and what it concluded.
- Findings — the validated security issues Neo produced, plus a count.
- References — identifiers that tie the artifact back to the originating task and its output stream.
Both delivery modes return this same artifact. The full field-by-field schema is in Webhook request below.
Choosing a delivery mode
- Use a webhook when you want results pushed downstream automatically — opening tickets, posting to a queue, or triggering the next step in a pipeline without polling.
- Use artifact (pull) when your system would rather fetch results on its own schedule, or when you cannot expose an inbound endpoint for Neo to call.
Push results to a webhook
The webhook target is a team-level setting, so every webhook-mode task in the team is delivered to the same URL. Updating it requires a team admin.
Set the webhook URL with PATCH /api/v1/teams:
curl -X PATCH https://neo.api.projectdiscovery.io/api/v1/teams \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: YOUR_API_KEY' \
--data '{
"webhook_url": "https://hooks.example.com/neo/task-completed"
}'
| Field | Purpose |
|---|
webhook_url | HTTPS endpoint Neo POSTs the completion artifact to. Pass an empty string to clear it. |
2. Trigger a task with webhook delivery
Set completion_delivery to webhook when you create the task:
curl -X POST https://neo.api.projectdiscovery.io/api/v1/tasks \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: YOUR_API_KEY' \
--data '{
"task": "Run a security review of https://staging.example.com",
"completion_delivery": "webhook"
}'
When the task finishes, Neo POSTs the artifact to your team webhook URL.
Webhook request
Neo sends a POST with a JSON body and these headers:
| Header | Value |
|---|
Content-Type | application/json |
X-Neo-Event | task.completed |
X-Neo-Completion-Delivery | webhook |
The body is the completion artifact, with an additional event field on webhook deliveries:
{
"schema_version": 1,
"event": "task.completed",
"completion_delivery": "webhook",
"task_id": "8c1a9f4e-2b6d-4f3a-9b2c-1e7d5a0c4f21",
"stream_id": "01J9...",
"status": "completed",
"completed_at": "2026-06-25T17:42:11Z",
"issue_count": 3,
"summary": "Reviewed the staging environment and confirmed 3 issues...",
"findings": [
{
"title": "Reflected XSS in search parameter",
"severity": "high",
"description": "...",
"evidence": []
}
]
}
| Field | Type | Description |
|---|
schema_version | integer | Payload schema version. Currently 1. |
event | string | task.completed. Present on webhook deliveries only. |
completion_delivery | string | The delivery mode the task was created with (webhook or artifact). |
task_id | string (uuid) | The task that produced the artifact. |
stream_id | string | Identifier for the task’s output stream, when available. |
status | string | Final task status, e.g. completed. |
completed_at | string (ISO 8601) | When the task finished. |
issue_count | integer | Number of findings in findings. |
summary | string | Human-readable summary of the task outcome. |
findings | array | Structured issue objects with title, severity, description, evidence, and remediation. See the API Reference for the full issue shape. |
Pull results via the API
If you would rather fetch results yourself, create the task with completion_delivery set to artifact:
curl -X POST https://neo.api.projectdiscovery.io/api/v1/tasks \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: YOUR_API_KEY' \
--data '{
"task": "Run a security review of https://staging.example.com",
"completion_delivery": "artifact"
}'
Once the task completes, retrieve the persisted artifact:
curl https://neo.api.projectdiscovery.io/api/v1/tasks/TASK_ID/artifacts \
--header 'X-Api-Key: YOUR_API_KEY'
The response wraps the same artifact payload described above:
{
"task_id": "8c1a9f4e-2b6d-4f3a-9b2c-1e7d5a0c4f21",
"completion_delivery": "artifact",
"artifact": {
"schema_version": 1,
"completion_delivery": "artifact",
"task_id": "8c1a9f4e-2b6d-4f3a-9b2c-1e7d5a0c4f21",
"status": "completed",
"completed_at": "2026-06-25T17:42:11Z",
"issue_count": 3,
"summary": "...",
"findings": []
}
}
Pass ?refresh=true to rebuild the artifact from the task’s current issues if findings were updated after completion:
curl "https://neo.api.projectdiscovery.io/api/v1/tasks/TASK_ID/artifacts?refresh=true" \
--header 'X-Api-Key: YOUR_API_KEY'
Delivery and security guarantees
- Best-effort delivery. Webhook delivery does not block or fail the task. If your endpoint is unreachable or returns a non-2xx status, the task still completes normally — so for guaranteed durability, treat the persisted artifact (
GET /api/v1/tasks/{id}/artifacts) as the source of truth.
- Timeout. Neo waits up to 15 seconds for your endpoint to respond. Acknowledge quickly with a 2xx and process the payload asynchronously.
- HTTPS only. Webhook URLs must use
http or https. Use HTTPS in production.
- SSRF protection. Neo will not call private or internal targets. URLs resolving to
localhost, link-local, RFC 1918 private ranges, or cloud metadata endpoints are blocked.
Use cases
| Use case | How Neo helps |
|---|
| Ticketing automation | Receive findings the moment a task completes and open issues in your tracker with full evidence and severity. |
| Pipeline orchestration | Trigger downstream remediation, notification, or verification steps from the task.completed webhook. |
| Storage and reporting | Persist artifacts into your data lake or reporting store for historical analysis and audit trails. |
| Polling-free integrations | Push results downstream automatically instead of repeatedly polling task status. |
| Pull-based ingestion | Fetch artifacts on your own schedule when you cannot expose an inbound webhook endpoint. |