Browse documentation

Webhooks

Receive operation, tunnel and device events at your HTTPS endpoint.

Create a webhook

POST/webhookswebhooks:write

Creates an endpoint and returns its signing secret once. Store it immediately. Idempotency required.

FieldUse
urlRequired public HTTPS URL.
eventsRequired non-empty array of event names.
descriptionOptional label for your team.
JSON body
{
  "url": "https://example.com/webhooks/portwarp",
  "events": ["command.succeeded", "command.failed", "device.offline"],
  "description": "Production automation"
}

List and get webhooks

GET/webhookswebhooks:read

Lists the key's webhook endpoints.

GET/webhooks/{webhook_id}webhooks:read

Returns one webhook endpoint.

Update or delete a webhook

PATCH/webhooks/{webhook_id}webhooks:write

Updates any supplied url, events, description or status. Status is enabled or disabled.

DELETE/webhooks/{webhook_id}webhooks:write

Removes the endpoint. Idempotency required.

Test and inspect deliveries

POST/webhooks/{webhook_id}/testwebhooks:write

Schedules a test event and returns 202. Idempotency required.

GET/webhooks/{webhook_id}/deliverieswebhooks:read

Returns up to 100 recent delivery attempts without their event payloads.

Events

command.queued, command.claimed, command.running, command.succeeded, command.failed, command.expired, command.canceled, tunnel.started, tunnel.stopped, device.online and device.offline.

Event body

JSON
{
  "id": "EVENT_ID",
  "type": "command.succeeded",
  "created_at": "2026-09-09T18:00:03+00:00",
  "data": {"operation_id":"OPERATION_ID","device_id":"DEVICE_ID"}
}

Verify the signature

Read Portwarp-Webhook-Id, Portwarp-Webhook-Timestamp and Portwarp-Webhook-Signature. Compute HMAC-SHA256 over id.timestamp.raw_body with the endpoint secret and compare it to the hexadecimal value after v1=.

Pseudocode
signed = webhook_id + "." + timestamp + "." + raw_request_body
expected = hex(hmac_sha256(webhook_secret, signed))
valid = constant_time_equal("v1=" + expected, signature_header)

Reject timestamps older than five minutes and deduplicate by event ID. The same event ID may be delivered again when Portwarp retries.

Delivery retries

Return a 2xx response quickly. Failed deliveries are retried after approximately 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours.