Skip to main content
Pi can push job terminal-state events to your systems so automation platforms (n8n, agent runners, internal pipelines) can react without polling.

Endpoints

MethodPathDescription
GET/api/v1/webhooksList all webhooks for your organization
POST/api/v1/webhooksRegister a new webhook endpoint
PATCH/api/v1/webhooks/:idEnable or disable a webhook

Register a webhook

POST to /api/v1/webhooks with the target URL and a shared secret:

Request fields

endpoint_url
string
required
The HTTPS URL Pi will POST events to. Must use https.
secret
string
required
Shared secret used to sign delivery payloads (HMAC SHA-256). Store this securely — Pi will not return it after creation.

Example

curl -X POST "https://api.example.com/api/v1/webhooks" \
  -H "Authorization: Bearer pi_live_***" \
  -H "Content-Type: application/json" \
  -d '{"endpoint_url":"https://example.com/pi/webhooks","secret":"whsec_..."}'

List webhooks

Retrieve all webhooks registered for the authenticated organization:
curl -X GET "https://api.example.com/api/v1/webhooks" \
  -H "Authorization: Bearer pi_live_***"

Enable or disable a webhook

PATCH /api/v1/webhooks/:id with is_active to toggle a webhook:

Request fields

is_active
boolean
required
Set to true to enable deliveries or false to pause them.
curl -X PATCH "https://api.example.com/api/v1/webhooks/<webhook_id>" \
  -H "Authorization: Bearer pi_live_***" \
  -H "Content-Type: application/json" \
  -d '{"is_active":false}'

Event delivery

Event types

Pi delivers these events when async workers reach a terminal state:
EventTrigger
job.completedJob finished successfully
job.failedJob encountered an unrecoverable error

Delivery headers

Every delivery includes:
HeaderDescription
X-Pi-EventEvent type (job.completed or job.failed)
X-Pi-SignatureHMAC SHA-256 of the raw JSON body using your secret, formatted as sha256=<hex>

Payload shape

id
string
Unique event id (evt_pi_…). Use this to deduplicate deliveries.
object
string
Always event.
type
string
Event type: job.completed or job.failed.
created_at
number
Unix timestamp in seconds.
data.job
object
{
  "id": "evt_pi_...",
  "object": "event",
  "type": "job.completed",
  "created_at": 1760000000,
  "data": {
    "job": {
      "id": "<job_id>",
      "type": "avatar_generation",
      "status": "completed",
      "result_url": null,
      "error_log": null,
      "payload": {
        "phase": "completed",
        "image_url": "https://...png",
        "input": {
          "prompt": "...",
          "reference_image_count": 0,
          "client_reference_id": "...",
          "metadata": { "workflow": "..." }
        }
      }
    }
  }
}

Verifying signatures

Verify the X-Pi-Signature header before processing any delivery:
import crypto from "crypto";

export function verifyPiWebhookSignature(params: {
  secret: string;
  rawBody: string;
  signatureHeader: string | null;
}) {
  if (!params.signatureHeader) return false;
  if (!params.signatureHeader.startsWith("sha256=")) return false;

  const expected = crypto
    .createHmac("sha256", params.secret)
    .update(params.rawBody)
    .digest("hex");

  const received = params.signatureHeader.slice("sha256=".length);
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
}
Always verify the signature before trusting the payload. Reject any delivery where verification fails.

Retry model

Pi retries delivery on non-2xx responses. Make your webhook handler idempotent by deduplicating on the evt_pi_* event id — the same event may be delivered more than once.

LiveKit webhook

Pi also exposes a signed LiveKit webhook endpoint for voice session events:
POST /api/v1/voice/webhooks/livekit
This endpoint verifies LiveKit’s native signature and is separate from the general webhook registry above. Configure its target URL in your LiveKit project settings pointing at your Pi deployment.