Network Pulse Ingest API
Last updated 16 August 2026
Most people should install the InfraCue Collector instead of reading this page. The guided setup in Network Pulse → Set up installs it, fills in the credentials, and confirms the first check-in for you.
This page is for the other case: you already run a monitoring tool and would rather forward what it already knows than run a second agent. Everything below is what the collector itself sends, so anything that can POST signed JSON can speak it.
Zabbix and Nagios have ready-made handlers — pick that source when you create the connection and follow its setup guide instead of mapping fields by hand. For PRTG, SolarWinds, ManageEngine or anything else, use the generic webhook below and map your tool's fields to this payload.
Before you start: create a connection in Network Pulse → Connections and choose Generic webhook. That gives you an integration key and a secret. The secret is shown once and never redisplayed — if you lose it, rotate the connection for a fresh pair.
Endpoint
POST https://<host>/<workspace-slug>/api/network-pulse/ingest.php
Content-Type: application/json
The workspace comes from the URL path, not from the body. A request without the slug cannot identify a tenant and is rejected before the credentials are looked at.
Headers
| Header | Value |
|---|---|
X-InfraCue-Integration-Key |
The public key from the connection. |
X-InfraCue-Timestamp |
Unix epoch seconds, UTC, within ±300 s of our clock. |
X-InfraCue-Signature |
sha256= followed by 64 lowercase hex characters. See below. |
X-InfraCue-Request-Id |
Optional. Any opaque id; we echo it back on the response and in our logs, which is how a failure on your machine gets matched to a line in ours without correlating by wall-clock time across two timezones. |
Signing
The signature is an HMAC-SHA256 over the timestamp, a literal full stop, and the raw request body exactly as sent — sign the bytes you will transmit, not a re-serialised copy of them, or whitespace differences will break the comparison.
signature = "sha256=" + hex( hmac_sha256( timestamp + "." + raw_body, secret ) )
Two failures account for nearly every 401 we see:
- Local time instead of UTC. Windows PowerShell 5.1's
Get-Date -UFormat %sreturns a local-time epoch, which in IST is 19,800 seconds outside a 300-second tolerance. Use[DateTimeOffset]::UtcNow.ToUnixTimeSeconds(). - Re-encoding the body after signing. Sign last, send what you signed.
Bash
TS=$(date -u +%s)
BODY='{"events":[{"device_id":"sw-core-01","status":"down"}]}'
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)
curl -sS -X POST "https://infracue.com/$SLUG/api/network-pulse/ingest.php" \
-H 'Content-Type: application/json' \
-H "X-InfraCue-Integration-Key: $KEY" \
-H "X-InfraCue-Timestamp: $TS" \
-H "X-InfraCue-Signature: sha256=$SIG" \
--data-raw "$BODY"
PowerShell
$ts = [string][System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
$body = '{"events":[{"device_id":"sw-core-01","status":"down"}]}'
$hmac = [System.Security.Cryptography.HMACSHA256]::new([Text.Encoding]::UTF8.GetBytes($secret))
$sig = ($hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes("$ts.$body")) |
ForEach-Object { $_.ToString('x2') }) -join ''
Invoke-RestMethod -Method Post -Uri "https://infracue.com/$slug/api/network-pulse/ingest.php" `
-ContentType 'application/json' `
-Headers @{
'X-InfraCue-Integration-Key' = $key
'X-InfraCue-Timestamp' = $ts
'X-InfraCue-Signature' = "sha256=$sig"
} -Body $body
Body
Either an object with an events array, or a single event object on its own.
At most 100 events per request; the body must be under 1 MB.
{
"events": [
{
"device_id": "sw-core-01",
"name": "Core switch, Mumbai BKC",
"device_type": "switch",
"host": "10.20.0.1",
"location_name": "Mumbai HQ - BKC",
"status": "down",
"severity": "critical",
"event_type": "status",
"title": "ICMP unreachable",
"message": "3 consecutive probes failed",
"occurred_at": "2026-08-16T09:41:07Z",
"affects_state": true,
"metadata": { "probe": "icmp", "loss_pct": 100 }
}
]
}
| Field | Required | Notes |
|---|---|---|
device_id | Yes | Your stable identifier for the device, up to 160 characters. This is the key we match on, so keep it constant across restarts — a changing id creates a new device every time and burns through your plan's device count. |
status | Yes in practice | One of up, down, degraded,
maintenance, unknown. Anything else becomes
unknown rather than being rejected. |
name | No | Display name, up to 160 characters. Defaults to device_id. |
device_type | No | Free text, up to 80 characters. |
host | No | IP or hostname, up to 255 characters. |
location_name | No | Up to 160 characters. location is accepted as an alias. |
severity | No | info, warning or critical. Defaults to
info. |
event_type | No | Defaults to status. |
title | No | Up to 180 characters. |
message | No | Up to 2,000 characters. |
occurred_at | No | Any format PHP's DateTime parses; ISO 8601 is safest. Defaults to
our receipt time. |
event_id | No | Your own id for this event, up to 190 characters. Supply it and retries are deduplicated. Omit it and we derive one by hashing the event, which deduplicates identical retries but not semantically duplicate ones. |
affects_state | No | Defaults to true. Send false for an event that is worth
recording but says nothing about whether the device is healthy — a config
change, an agent restart, a scheduled job finishing. We store it on the device's
timeline and leave its status, severity and last-changed time untouched, and raise
no ticket. Use it rather than sending a status you do not mean: device state is
last-write-wins, so an informational up would clear a real outage. |
metadata | No | Object, first 30 keys kept. Shown on the device detail panel. |
deviceId, occurredAt, eventId,
deviceType and eventType are accepted as camelCase aliases.
Responses
Always JSON, always with a boolean success.
200 { "success": true, "result": { "accepted": 1, "duplicates": 0,
"capacity_limited": 0, "samples": 0 } }
| Status | Means |
|---|---|
400 | Malformed body, bad JSON, or no valid device events in it. |
401 | Unknown or revoked key, bad signature, or a timestamp outside tolerance. |
405 | Not a POST. |
413 | Empty body, or larger than 1 MB. |
422 | Workspace unavailable, Network Pulse not enabled, or rate limited. |
500 | Our fault. The response carries no detail; your request id will be in our log. |
capacity_limited counts devices we refused because the workspace is at its
plan device limit. It is not an error — the rest of the batch is still stored.
Rate limit
300 requests per minute per connection. Beyond that you get 422 until the
minute rolls over. Batch your events rather than sending one request per device.
What we do with it
- A device whose status changes gets a state-change record and, if your alert rules say so, a ticket in the right queue with the priority you chose.
- A batch where nothing changed state is the normal case and is accepted as such.
- We never call into your network. Everything is inbound from you.
Something here not matching what the endpoint does? Tell us — that is a bug in this page.