Sign in Create account
API reference

Webhooks API

Webhook endpoints, the events delivered to them, delivery attempts and replay.

Register URLs to receive events in real time. How to verify the signature, the format of each event and the retry policy are in the Webhooks guide.

The number of webhooks comes from the plan (webhooks): creating one beyond the limit answers 403 plan_limit_reached. If the plan drops below what the team has, the newest webhooks above the limit get paused_by_plan: true: they stay in the list and can be read and deleted, but they receive no events and cannot be changed, tested or have their secret rotated (403 paused_by_plan). They come back on their own when the plan or the count allows it.

#List webhooks

GET/webhooks

Full-access key

Accepts limit, after and before. The signing secret does not appear in the list. status is enabled or disabled and, when disabled, disabled_reason says why: manual (paused by a person) or failing (disabled after repeated failures).

GET /webhooks

cURL

curl https://api.postal.so/webhooks \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/webhooks', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/webhooks",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('webhooks', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Response
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "webhook",
      "id": "0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b",
      "paused_by_plan": false,
      "endpoint": "https://acmestore.com/webhooks/postal",
      "events": ["email.delivered", "email.bounced", "email.complained"],
      "skip_sources": [],
      "status": "enabled",
      "disabled_reason": null,
      "disabled_at": null,
      "failing_since": null,
      "last_success_at": "2026-10-24T13:02:14.000000Z",
      "created_at": "2026-10-03T10:00:00.000000Z"
    }
  ]
}

#Create a webhook

POST/webhooks

Full-access key

Registers the endpoint and returns the signing_secret (whsec_…) used to sign the deliveries.

Request body

  • endpointstringrequired
    Public HTTPS URL, up to 2048 characters. Private network addresses are refused at delivery.
  • eventsstring[]required
    One or more event types, without repeats: email.sent, email.delivered, email.delivery_delayed, email.bounced, email.complained, email.opened, email.clicked, email.replied, email.failed, email.scheduled, email.suppressed, email.received, domain.created, domain.updated, domain.deleted, suppression.added, suppression.removed, contact.created, contact.updated, contact.deleted, contact.segment_entered, contact.segment_exited, form.submitted, broadcast.sent, sending.paused, sending.resumed, inbox.thread.created, inbox.thread.updated, inbox.thread.deleted, inbox.draft.created. When each one happens: the webhooks guide.
  • skip_sourcesstring[]
    Up to 20 change sources that this endpoint should not receive in the contact.created, contact.updated and contact.deleted events. Each source has up to 64 characters (lowercase letters, numbers, ., _, : or -) and can end with * to work as a prefix, such as crm:*. It keeps a two-way sync from receiving back the changes it made itself: see the source of changes.

Errors

  • 403plan_limit_reached
    The team already has as many webhooks as the plan allows.
  • 422validation_error
    A URL that is not HTTPS, an empty list, an unknown or repeated event, or a source in an invalid format.
POST /webhooks

cURL

curl -X POST https://api.postal.so/webhooks \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "endpoint": "https://acmestore.com/webhooks/postal",
    "events": ["email.delivered", "email.bounced", "email.complained"]
  }'

Node.js

const response = await fetch('https://api.postal.so/webhooks', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    endpoint: 'https://acmestore.com/webhooks/postal',
    events: ['email.delivered', 'email.bounced', 'email.complained'],
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/webhooks",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "endpoint": "https://acmestore.com/webhooks/postal",
        "events": ["email.delivered", "email.bounced", "email.complained"],
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('webhooks', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'endpoint' => 'https://acmestore.com/webhooks/postal',
        'events' => ['email.delivered', 'email.bounced', 'email.complained'],
    ],
]);

$data = json_decode($response->getBody(), true);
201Response
{
  "object": "webhook",
  "id": "0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b",
  "signing_secret": "whsec_c2VncmVkby1kZS1leGVtcGxvLTEyMzQ1Ng=="
}

#Get a webhook

GET/webhooks/{id}

Full-access key

Returns the webhook with its current signing_secret.

Path parameters

  • idstringrequired
    Webhook ID.

Errors

  • 404not_found
    The resource does not exist or belongs to another team.
GET /webhooks/{id}

cURL

curl https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Response
{
  "object": "webhook",
  "id": "0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b",
  "paused_by_plan": false,
  "endpoint": "https://acmestore.com/webhooks/postal",
  "events": ["email.delivered", "email.bounced", "email.complained"],
  "skip_sources": [],
  "status": "enabled",
  "disabled_reason": null,
  "disabled_at": null,
  "failing_since": null,
  "last_success_at": "2026-10-24T13:02:14.000000Z",
  "created_at": "2026-10-03T10:00:00.000000Z",
  "signing_secret": "whsec_c2VncmVkby1kZS1leGVtcGxvLTEyMzQ1Ng=="
}

#Update a webhook

PATCH/webhooks/{id}

Full-access key

Changes the URL or the events, or pauses the webhook. With disabled, no event is delivered (not even the pending retries) and disabled_reason becomes manual. {"status": "enabled"} reactivates a paused webhook, or one disabled because of failures, and clears disabled_reason and failing_since.

Path parameters

  • idstringrequired
    Webhook ID.

Request body

  • endpointstring
    New HTTPS URL.
  • eventsstring[]
    New list of events (replaces the previous one).
  • skip_sourcesstring[] | null
    New list of sources to skip (same rules as Create a webhook). Send [] or null to clear it.
  • statusstring
    enabled or disabled.

Errors

  • 403paused_by_plan
    The webhook is paused because the team went over the plan limit. Delete other webhooks or change plans.
  • 422validation_error
    A field is invalid. The message says which one.
  • 404not_found
    The resource does not exist or belongs to another team.
PATCH /webhooks/{id}

cURL

curl -X PATCH https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "disabled"
  }'

Node.js

const response = await fetch('https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    status: 'disabled',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "status": "disabled",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'status' => 'disabled',
    ],
]);

$data = json_decode($response->getBody(), true);
200Response
{
  "object": "webhook",
  "id": "0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b"
}

#Delete a webhook

DELETE/webhooks/{id}

Full-access key

Path parameters

  • idstringrequired
    Webhook ID.

Errors

  • 404not_found
    The resource does not exist or belongs to another team.
DELETE /webhooks/{id}

cURL

curl -X DELETE https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Response
{
  "object": "webhook",
  "id": "0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b",
  "deleted": true
}

#Rotate the signing secret

POST/webhooks/{id}/signing-secret/rotate

Full-access key

Generates a new signing_secret, which takes effect immediately. Update your server right after: deliveries signed with the new secret fail verification with the old one.

Path parameters

  • idstringrequired
    Webhook ID.

Errors

  • 403paused_by_plan
    The webhook is paused by the plan.
  • 404not_found
    The resource does not exist or belongs to another team.
POST /webhooks/{id}/signing-secret/rotate

cURL

curl -X POST https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/signing-secret/rotate \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/signing-secret/rotate', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/signing-secret/rotate",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/signing-secret/rotate', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Response
{
  "object": "webhook",
  "id": "0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b",
  "signing_secret": "whsec_bm92by1zZWdyZWRvLWRlLWV4ZW1wbG8tOTg3Ng=="
}

#Send a test event

POST/webhooks/{id}/test

Full-access key

Immediately sends a webhook.test event to the endpoint, signed like the others, so you can check the URL and the signature verification. It is a single attempt, with no retries, and works even when the webhook is disabled. It shows up in the events list and does not count toward automatic disabling.

Path parameters

  • idstringrequired
    Webhook ID.

Errors

  • 403paused_by_plan
    The webhook is paused by the plan.
  • 404not_found
    The resource does not exist or belongs to another team.
POST /webhooks/{id}/test

cURL

curl -X POST https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/test \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/test', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/test",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/test', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
202Response
{
  "object": "webhook_event",
  "id": "019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c",
  "type": "webhook.test"
}

#List a webhook's events

GET/webhooks/{id}/events

Full-access key

The events sent to this webhook in the last 30 days, with their delivery status: pending, attempting (failed and has another attempt at next_attempt_at), success or failed (ran out of attempts, or the webhook was disabled). Accepts limit, after and before.

Path parameters

  • idstringrequired
    Webhook ID.

Errors

  • 404not_found
    The resource does not exist or belongs to another team.
GET /webhooks/{id}/events

cURL

curl https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Response
{
  "object": "list",
  "has_more": true,
  "data": [
    {
      "object": "webhook_event",
      "id": "019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c",
      "type": "email.delivered",
      "created_at": "2026-10-24T13:02:14.000000Z",
      "status": "success",
      "attempts": 1,
      "next_attempt_at": null
    }
  ]
}

#Get an event

GET/webhooks/{id}/events/{event_id}

Full-access key

Returns the event with the exact payload that was (or will be) sent.

Path parameters

  • idstringrequired
    Webhook ID.
  • event_idstringrequired
    Event ID.

Errors

  • 404not_found
    The resource does not exist or belongs to another team.
GET /webhooks/{id}/events/{event_id}

cURL

curl https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Response
{
  "object": "webhook_event",
  "id": "019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c",
  "type": "email.delivered",
  "created_at": "2026-10-24T13:02:14.000000Z",
  "status": "success",
  "attempts": 1,
  "next_attempt_at": null,
  "payload": {
    "type": "email.delivered",
    "created_at": "2026-10-24T13:02:14.120Z",
    "data": {
      "email_id": "019a1b2c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
      "created_at": "2026-10-24T13:02:11.000Z",
      "from": "Acme Store <[email protected]>",
      "to": ["[email protected]"],
      "subject": "Your order #10482 is out for delivery",
      "tags": {
        "category": "shipping"
      }
    }
  }
}

#List delivery attempts

GET/webhooks/{id}/events/{event_id}/attempts

Full-access key

Each delivery attempt with the HTTP status, the beginning of your server's response (up to 2,000 characters), the connection error, if there was one, and the duration.

Path parameters

  • idstringrequired
    Webhook ID.
  • event_idstringrequired
    Event ID.

Errors

  • 404not_found
    The resource does not exist or belongs to another team.
GET /webhooks/{id}/events/{event_id}/attempts

cURL

curl https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c/attempts \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c/attempts', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c/attempts",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c/attempts', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Response
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "019a1b2c-5e70-7b91-8c2d-3e4f5a6b7c8d",
      "http_status_code": 200,
      "response": "{\"ok\":true}",
      "error": null,
      "duration_ms": 184,
      "sent_at": "2026-10-24T13:02:14.000000Z"
    }
  ]
}

#Replay an event

POST/webhooks/{id}/events/{event_id}/replay

Full-access key

Delivers the event again right away, even if it already succeeded or failed for good. The attempt counter starts over. The delivery uses the same message ID (webhook-id), so your server can recognize the repeat. While the webhook is disabled nothing is delivered: re-enable it first with PATCH /webhooks/{id}.

Path parameters

  • idstringrequired
    Webhook ID.
  • event_idstringrequired
    Event ID.

Errors

  • 403paused_by_plan
    The webhook is paused by the plan.
  • 404not_found
    The resource does not exist or belongs to another team.
POST /webhooks/{id}/events/{event_id}/replay

cURL

curl -X POST https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c/replay \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c/replay', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c/replay",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('webhooks/0199f6d2-3c4d-7e5f-8a6b-7c8d9e0f1a2b/events/019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c/replay', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Response
{
  "object": "webhook_event",
  "id": "019a1b2c-5e6f-7a80-9b1c-2d3e4f5a6b7c"
}