Webhooks

Receive events at your own endpoints through signed, retried and recorded deliveries. Includes replays, test deliveries and a relay for sandbox events.


GET/v2/webhooks

List webhook endpoints

Returns the app's webhook endpoints. A live key returns live endpoints, and a test key returns test endpoints, which receive events from the sandbox. Endpoints created in the dashboard before v2 are included too, and use v1 payloads until you switch them.

In the sandbox: A test key lists test endpoints, which receive events from the sandbox. Every sandbox includes the sandbox inbox (whk_sbx671d37d5e9995de7b6be), an endpoint hosted by Pyas.

Sends the request to your sandbox, never to live data.

Errors

Request

GET
/v2/webhooks
curl 'https://api.pyas.io/v2/webhooks' \  -H "x-api-key: $PYAS_API_KEY"

Response

200 OK
{  "success": true,  "data": [    {      "webhookId": "whk_sbx671d37d5e9995de7b6be",      "url": "pyas://sandbox-inbox",      "description": "The sandbox inbox, an endpoint hosted by Pyas that receives every sandbox event. To read what it received, call GET /v2/webhooks/deliveries.",      "mode": "test",      "payloadVersion": "v2",      "events": [        "*"      ],      "triggers": [],      "status": "active",      "disabledReason": null,      "disabledAt": null,      "auth": null,      "signingSecret": {        "last4": "9286"      },      "consecutiveFailures": 0,      "createdAt": "2026-11-14T20:00:24.351Z",      "updatedAt": "2026-11-14T20:00:24.351Z"    }  ],  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

POST/v2/webhooks

Create a webhook endpoint

Creates a webhook endpoint. Pyas sends each event the endpoint subscribes to as a POST request, signed in the Pyas-Signature header with the signing secret returned in this response. Store the secret. You can retrieve it again later with GET /v2/webhooks/:webhookId/secret. To check that your endpoint works, send it a test delivery with POST /v2/webhooks/:webhookId/test.

In the sandbox: The response includes the signing secret. Next, send the endpoint a test delivery with POST /v2/webhooks/{webhookId}/test.

Sends the request to your sandbox, never to live data.

Headers

  • Name
    Idempotency-Key
    Type
    string
    Description

    A unique key that makes the request safe to retry. A retry with the same key returns the original response and does not make the change twice. Keys are kept for 24 hours.

Body

  • Name
    url
    Type
    string
    Required
    required
    Description

    The URL of your endpoint. Must use HTTPS, except for test endpoints, which also accept HTTP.

  • Name
    events
    Type
    array of string
    Description

    The event types to receive. Defaults to ["*"], which subscribes to all types, including types added later. At most 50 items.

  • Name
    description
    Type
    string | null
    Description
  • Name
    auth
    Type
    object | null
    Description

    A credential Pyas sends with every delivery, either as a custom header or as a bearer token.

  • Name
    payloadVersion
    Type
    string
    Description

    One of v2.

Errors

Request

POST
/v2/webhooks
curl -X POST 'https://api.pyas.io/v2/webhooks' \  -H "x-api-key: $PYAS_API_KEY" \  -H "content-type: application/json" \  -d '{  "url": "https://example.com/pyas/webhooks",  "events": [    "event.*",    "connection.broken"  ],  "description": "Calendar sync",  "auth": {    "type": "header",    "name": "X-Webhook-Key",    "value": "my-shared-secret"  }}'

Response

201 Created
{  "success": true,  "data": {    "endpoint": {      "webhookId": "004af03b-0f03-4e97-91df-852da33048d9",      "url": "https://example.com/pyas/webhooks",      "description": "Calendar sync",      "mode": "test",      "payloadVersion": "v2",      "events": [        "event.*",        "connection.broken"      ],      "triggers": [],      "status": "active",      "disabledReason": null,      "disabledAt": null,      "auth": {        "type": "header",        "name": "X-Webhook-Key"      },      "signingSecret": {        "last4": "Wazs"      },      "consecutiveFailures": 0,      "createdAt": "2026-11-14T20:00:25.699Z",      "updatedAt": "2026-11-14T20:00:25.699Z"    },    "signingSecret": "whsec_example"  },  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

GET/v2/webhooks/{webhookId}

Get a webhook endpoint

Sends the request to your sandbox, never to live data.

Path parameters

  • Name
    webhookId
    Type
    string
    Required
    required
    Description

Errors

Request

GET
/v2/webhooks/{webhookId}
curl 'https://api.pyas.io/v2/webhooks/whk_sbx671d37d5e9995de7b6be' \  -H "x-api-key: $PYAS_API_KEY"

Response

200 OK
{  "success": true,  "data": {    "webhookId": "whk_sbx671d37d5e9995de7b6be",    "url": "pyas://sandbox-inbox",    "description": "The sandbox inbox, an endpoint hosted by Pyas that receives every sandbox event. To read what it received, call GET /v2/webhooks/deliveries.",    "mode": "test",    "payloadVersion": "v2",    "events": [      "*"    ],    "triggers": [],    "status": "active",    "disabledReason": null,    "disabledAt": null,    "auth": null,    "signingSecret": {      "last4": "9286"    },    "consecutiveFailures": 0,    "createdAt": "2026-11-14T20:00:24.351Z",    "updatedAt": "2026-11-14T20:00:24.351Z"  },  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

PATCH/v2/webhooks/{webhookId}

Update a webhook endpoint

Updates the endpoint's URL, events, credential or description. You can also disable or re-enable it with status. Before re-enabling an endpoint, Pyas sends it a test delivery, and the endpoint stays disabled if that delivery fails. To switch an endpoint created before v2 to v2 payloads, set payloadVersion to "v2". After the switch, v1 no longer sends the endpoint anything, and it receives signed, retried v2 deliveries of the event types that its v1 triggers correspond to, unless you set events. The first time an endpoint switches, a signing secret is created and returned in this response.

Sends the request to your sandbox, never to live data.

Path parameters

  • Name
    webhookId
    Type
    string
    Required
    required
    Description

Headers

  • Name
    Idempotency-Key
    Type
    string
    Description

    A unique key that makes the request safe to retry. A retry with the same key returns the original response and does not make the change twice. Keys are kept for 24 hours.

Body

  • Name
    url
    Type
    string
    Description
  • Name
    events
    Type
    array of string
    Description

    At most 50 items.

  • Name
    description
    Type
    string | null
    Description
  • Name
    auth
    Type
    object | null
    Description

    A new credential, or null to remove it.

  • Name
    status
    Type
    string
    Description

    One of active, disabled.

  • Name
    payloadVersion
    Type
    string
    Description

    One of v1, v2.

Errors

Request

PATCH
/v2/webhooks/{webhookId}
curl -X PATCH 'https://api.pyas.io/v2/webhooks/whk_sbx671d37d5e9995de7b6be' \  -H "x-api-key: $PYAS_API_KEY" \  -H "content-type: application/json" \  -d '{  "events": [    "event.*",    "calendar.*"  ],  "description": "Sandbox inbox: events and calendars"}'

Response

200 OK
{  "success": true,  "data": {    "endpoint": {      "webhookId": "whk_sbx671d37d5e9995de7b6be",      "url": "pyas://sandbox-inbox",      "description": "Sandbox inbox: events and calendars",      "mode": "test",      "payloadVersion": "v2",      "events": [        "event.*",        "calendar.*"      ],      "triggers": [],      "status": "active",      "disabledReason": null,      "disabledAt": null,      "auth": null,      "signingSecret": {        "last4": "E2vA"      },      "consecutiveFailures": 0,      "createdAt": "2026-11-14T20:00:25.476Z",      "updatedAt": "2026-11-14T20:00:25.767Z"    }  },  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

DELETE/v2/webhooks/{webhookId}

Delete a webhook endpoint

Deletes the endpoint. Pyas stops sending deliveries to it, and its deliveries remain available for 30 days.

In the sandbox: This deletes the sandbox inbox. POST /v2/sandbox/reset creates it again.

Sends the request to your sandbox, never to live data.

Path parameters

  • Name
    webhookId
    Type
    string
    Required
    required
    Description

Headers

  • Name
    Idempotency-Key
    Type
    string
    Description

    A unique key that makes the request safe to retry. A retry with the same key returns the original response and does not make the change twice. Keys are kept for 24 hours.

Errors

Request

DELETE
/v2/webhooks/{webhookId}
curl -X DELETE 'https://api.pyas.io/v2/webhooks/whk_sbx671d37d5e9995de7b6be' \  -H "x-api-key: $PYAS_API_KEY"

Response

200 OK
{  "success": true,  "data": null,  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

GET/v2/webhooks/deliveries

List webhook deliveries

Returns deliveries from the last 30 days, newest first. Each delivery includes every attempt, with its status code, the first 1 KB of the response body, the time taken and any error. Use status=failed to list the dead-letter queue, which holds the deliveries that failed after every attempt.

Sends the request to your sandbox, never to live data.

Query parameters

  • Name
    webhookId
    Type
    string
    Description
  • Name
    status
    Type
    string
    Description

    Returns only deliveries with this status. One of pending, retrying, delivered, failed.

  • Name
    eventType
    Type
    string
    Description
  • Name
    eventId
    Type
    string
    Description
  • Name
    since
    Type
    string (date-time)
    Description

    An ISO 8601 time with a zone.

  • Name
    limit
    Type
    integer
    Description

    The maximum number of results in one page. From 1 to 100.

  • Name
    cursor
    Type
    string
    Description

    The nextCursor from the previous page.

Errors

Request

GET
/v2/webhooks/deliveries
curl 'https://api.pyas.io/v2/webhooks/deliveries?webhookId=whk_sbx671d37d5e9995de7b6be' \  -H "x-api-key: $PYAS_API_KEY"

Response

200 OK
{  "success": true,  "data": [    {      "deliveryId": "del_sbxf61f198fc5d9f0072a38",      "webhookId": "whk_sbx671d37d5e9995de7b6be",      "eventId": "evt_sbx0b0f2dbfa1198fcb366c",      "eventType": "webhook.ping",      "accountId": null,      "status": "delivered",      "attempts": [        {          "n": 1,          "at": "2026-11-14T20:00:23Z",          "httpStatus": 200,          "responseBody": "{\"received\":true,\"by\":\"the Pyas sandbox inbox\"}",          "responseTimeMs": 0,          "error": null        }      ],      "nextAttemptAt": null,      "replays": 0,      "createdAt": "2026-11-14T20:00:23Z",      "completedAt": "2026-11-14T20:00:23Z"    }  ],  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {    "nextCursor": null  }}

GET/v2/webhooks/event-types

List webhook event types

Returns every event type, a description of what it means, and a sample of the data object that a delivery of that type includes.

Sends the request to your sandbox, never to live data.

Errors

Request

GET
/v2/webhooks/event-types
curl 'https://api.pyas.io/v2/webhooks/event-types' \  -H "x-api-key: $PYAS_API_KEY"

Response

200 OK
{  "success": true,  "data": [    {      "type": "connection.created",      "description": "An account was connected for the first time. `data.object` is the account.",      "sample": {        "object": {          "accountId": "acc_sample",          "provider": "google",          "email": "avery.brooks@example.com",          "name": "Avery Brooks",          "status": "active",          "subjectId": "user_123",          "scopes": [            "https://www.googleapis.com/auth/calendar"          ],          "createdAt": "2026-10-15T20:00:24Z",          "updatedAt": "2026-11-14T20:00:24Z"        }      }    },    {      "type": "connection.reauthorized",      "description": "An account was connected again, after it stopped working or to grant more permissions. `data.object` is the account.",      "sample": {        "object": {          "accountId": "acc_sample",          "provider": "google",          "email": "avery.brooks@example.com",          "name": "Avery Brooks",          "status": "active",          "subjectId": "user_123",          "scopes": [            "https://www.googleapis.com/auth/calendar"          ],          "createdAt": "2026-10-15T20:00:24Z",          "updatedAt": "2026-11-14T20:00:24Z"        }      }    },    {      "type": "connection.broken",      "description": "An account stopped working because access was revoked or could not be renewed. The user needs to connect it again. `data.object` is the account.",      "sample": {        "object": {          "accountId": "acc_sample",          "provider": "google",          "email": "avery.brooks@example.com",          "name": "Avery Brooks",          "status": "inactive",          "subjectId": "user_123",          "scopes": [            "https://www.googleapis.com/auth/calendar"          ],          "createdAt": "2026-10-15T20:00:24Z",          "updatedAt": "2026-11-14T20:00:24Z"        }      }    },    {      "type": "connection.deleted",      "description": "An account was deleted from Pyas. `data.object` is the account as it was before it was deleted.",      "sample": {        "object": {          "accountId": "acc_sample",          "provider": "google",          "email": "avery.brooks@example.com",          "name": "Avery Brooks",          "status": "active",          "subjectId": "user_123",          "scopes": [            "https://www.googleapis.com/auth/calendar"          ],          "createdAt": "2026-10-15T20:00:24Z",          "updatedAt": "2026-11-14T20:00:24Z"        }      }    },    {      "type": "calendar.created",      "description": "A calendar was added to an account. `data.object` holds the calendar ID.",      "sample": {        "object": {          "id": "team@group.calendar.google.com"        }      }    },    {      "type": "calendar.updated",      "description": "A calendar was updated, for example its name, color or time zone. `data.object` holds the calendar ID. Read the calendar to get its new details.",      "sample": {        "object": {          "id": "team@group.calendar.google.com"        }      }    },    {      "type": "calendar.deleted",      "description": "A calendar was removed from an account. `data.object` holds the calendar ID.",      "sample": {        "object": {          "id": "team@group.calendar.google.com"        }      }    },    {      "type": "event.created",      "description": "An event was created. A new recurring series sends one webhook for the series. `data.object` holds the event ID and calendar ID. Read the event to get its details.",      "sample": {        "object": {          "id": "sample1a2b3c4d5e6f7g8h9i",          "calendarId": "primary",          "seriesId": null,          "isSeries": false        }      }    },    {      "type": "event.updated",      "description": "An event was updated. An update to a whole series sends one webhook for the series. `data.object` holds the event ID and calendar ID. Read the event to get its new details.",      "sample": {        "object": {          "id": "sample1a2b3c4d5e6f7g8h9i",          "calendarId": "primary",          "seriesId": null,          "isSeries": false        }      }    },    {      "type": "event.deleted",      "description": "An event was deleted or canceled. `data.object` holds the event ID and calendar ID.",      "sample": {        "object": {          "id": "sample1a2b3c4d5e6f7g8h9i",          "calendarId": "primary",          "seriesId": null,          "isSeries": false        }      }    },    {      "type": "invitation.replied",      "description": "An attendee responded to an invitation sent from a virtual calendar. `data.object` holds the event ID and calendar ID, and `data.reply` is the response.",      "sample": {        "object": {          "id": "sample1a2b3c4d5e6f7g8h9i",          "calendarId": "primary",          "seriesId": null,          "isSeries": false        },        "reply": {          "email": "sam.patel@example.org",          "response": "tentative",          "comment": "Running late from another meeting.",          "proposedTime": null        }      }    },    {      "type": "meeting.updated",      "description": "A Zoom meeting that Pyas created for an event was updated in Zoom. `data.object` is the meeting and the event it belongs to, and `data.previous` contains the previous values. Pyas doesn't update the calendar event. Update the event yourself if you want it to match.",      "sample": {        "object": {          "meetingId": "84512345678",          "uuid": "SnJ2bYn7QnWqUj3sSvKQ5A==",          "topic": "Customer onboarding",          "startsAt": "2026-11-15T21:00:00Z",          "durationMinutes": 60,          "timezone": "America/New_York",          "eventId": "sample1a2b3c4d5e6f7g8h9i",          "calendarRef": "primary",          "accountId": "acc_sample",          "zoomAccountId": "acc_zoom_sample"        },        "previous": {          "startsAt": "2026-11-15T20:00:00Z"        }      }    },    {      "type": "meeting.deleted",      "description": "A Zoom meeting that Pyas created for an event was deleted in Zoom, so the event's join link no longer works. `data.object` is the meeting and its event.",      "sample": {        "object": {          "meetingId": "84512345678",          "uuid": "SnJ2bYn7QnWqUj3sSvKQ5A==",          "topic": "Customer onboarding",          "startsAt": "2026-11-15T21:00:00Z",          "durationMinutes": 60,          "timezone": "America/New_York",          "eventId": "sample1a2b3c4d5e6f7g8h9i",          "calendarRef": "primary",          "accountId": "acc_sample",          "zoomAccountId": "acc_zoom_sample"        }      }    },    {      "type": "meeting.started",      "description": "A Zoom meeting that Pyas created for an event started. `data.object` is the meeting and its event.",      "sample": {        "object": {          "meetingId": "84512345678",          "uuid": "SnJ2bYn7QnWqUj3sSvKQ5A==",          "topic": "Customer onboarding",          "startsAt": "2026-11-15T21:00:00Z",          "durationMinutes": 60,          "timezone": "America/New_York",          "eventId": "sample1a2b3c4d5e6f7g8h9i",          "calendarRef": "primary",          "accountId": "acc_sample",          "zoomAccountId": "acc_zoom_sample",          "startedAt": "2026-11-14T20:00:24Z"        }      }    },    {      "type": "meeting.ended",      "description": "A Zoom meeting that Pyas created for an event ended. `data.object` is the meeting and its event, with `startedAt` and `endedAt`.",      "sample": {        "object": {          "meetingId": "84512345678",          "uuid": "SnJ2bYn7QnWqUj3sSvKQ5A==",          "topic": "Customer onboarding",          "startsAt": "2026-11-15T21:00:00Z",          "durationMinutes": 60,          "timezone": "America/New_York",          "eventId": "sample1a2b3c4d5e6f7g8h9i",          "calendarRef": "primary",          "accountId": "acc_sample",          "zoomAccountId": "acc_zoom_sample",          "startedAt": "2026-11-14T19:05:24Z",          "endedAt": "2026-11-14T20:00:24Z"        }      }    },    {      "type": "meeting.participant_joined",      "description": "A participant joined a Zoom meeting that Pyas created for an event. `data.participant` identifies the participant.",      "sample": {        "object": {          "meetingId": "84512345678",          "uuid": "SnJ2bYn7QnWqUj3sSvKQ5A==",          "topic": "Customer onboarding",          "startsAt": "2026-11-15T21:00:00Z",          "durationMinutes": 60,          "timezone": "America/New_York",          "eventId": "sample1a2b3c4d5e6f7g8h9i",          "calendarRef": "primary",          "accountId": "acc_sample",          "zoomAccountId": "acc_zoom_sample"        },        "participant": {          "name": "Sam Patel",          "email": "sam.patel@example.org",          "joinedAt": "2026-11-14T20:00:24Z"        }      }    },    {      "type": "meeting.participant_left",      "description": "A participant left a Zoom meeting that Pyas created for an event. `data.participant` identifies the participant.",      "sample": {        "object": {          "meetingId": "84512345678",          "uuid": "SnJ2bYn7QnWqUj3sSvKQ5A==",          "topic": "Customer onboarding",          "startsAt": "2026-11-15T21:00:00Z",          "durationMinutes": 60,          "timezone": "America/New_York",          "eventId": "sample1a2b3c4d5e6f7g8h9i",          "calendarRef": "primary",          "accountId": "acc_sample",          "zoomAccountId": "acc_zoom_sample"        },        "participant": {          "name": "Sam Patel",          "email": "sam.patel@example.org",          "leftAt": "2026-11-14T20:00:24Z"        }      }    },    {      "type": "recording.ready",      "description": "The cloud recording of a Zoom meeting that Pyas created for an event is ready. `data.object` lists the recording files with their type, size, start and end times, and share link.",      "sample": {        "object": {          "meetingId": "84512345678",          "uuid": "SnJ2bYn7QnWqUj3sSvKQ5A==",          "topic": "Customer onboarding",          "startsAt": "2026-11-15T21:00:00Z",          "durationMinutes": 60,          "timezone": "America/New_York",          "eventId": "sample1a2b3c4d5e6f7g8h9i",          "calendarRef": "primary",          "accountId": "acc_sample",          "zoomAccountId": "acc_zoom_sample",          "shareUrl": "https://zoom.us/rec/share/sample",          "files": [            {              "id": "rec-1",              "type": "shared_screen_with_speaker_view",              "fileType": "MP4",              "sizeBytes": 48213002,              "startedAt": "2026-11-14T19:05:24Z",              "endedAt": "2026-11-14T20:00:24Z",              "playUrl": "https://zoom.us/rec/play/sample"            },            {              "id": "rec-2",              "type": "audio_transcript",              "fileType": "TRANSCRIPT",              "sizeBytes": 18204,              "startedAt": "2026-11-14T19:05:24Z",              "endedAt": "2026-11-14T20:00:24Z",              "playUrl": null            }          ]        }      }    },    {      "type": "api_key.expiring",      "description": "An API key expires soon. Sent a week before the key's expiry date, or an hour before a rotated key's grace period ends. `data.object` is the key, without its secret value.",      "sample": {        "object": {          "keyId": "key_sample",          "name": "production",          "mode": "live",          "expiresAt": "2026-11-14T21:00:24Z",          "rotatedTo": "key_sample2"        }      }    },    {      "type": "api_key.revoked",      "description": "A rotated API key's grace period ended, and the key no longer works. `data.object` is the key.",      "sample": {        "object": {          "keyId": "key_sample",          "name": "production",          "mode": "live",          "expiresAt": "2026-11-14T20:00:24Z",          "rotatedTo": "key_sample2"        }      }    },    {      "type": "webhook.ping",      "description": "A test delivery. Pyas sends it when you request one and when an endpoint is enabled again.",      "sample": {        "object": {          "message": "A test delivery from Pyas."        }      }    }  ],  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

GET/v2/webhooks/events

List webhook events

Returns events from the last 30 days, newest first, whether or not an endpoint received them. Use it to catch up after downtime or to check what happened. A test key returns the sandbox's events.

Sends the request to your sandbox, never to live data.

Query parameters

  • Name
    type
    Type
    string
    Description

    An event type, or a family such as event.*.

  • Name
    accountId
    Type
    string
    Description
  • Name
    since
    Type
    string (date-time)
    Description

    Returns only events created at or after this time.

  • Name
    limit
    Type
    integer
    Description

    The maximum number of results in one page. From 1 to 100.

  • Name
    cursor
    Type
    string
    Description

    The nextCursor from the previous page.

Errors

Request

GET
/v2/webhooks/events
curl 'https://api.pyas.io/v2/webhooks/events?type=webhook.ping' \  -H "x-api-key: $PYAS_API_KEY"

Response

200 OK
{  "success": true,  "data": [    {      "eventId": "evt_sbx0b0f2dbfa1198fcb366c",      "type": "webhook.ping",      "accountId": null,      "subjectId": null,      "provider": null,      "createdAt": "2026-11-14T20:00:23Z",      "payload": {        "id": "evt_sbx0b0f2dbfa1198fcb366c",        "type": "webhook.ping",        "apiVersion": "2",        "createdAt": "2026-11-14T20:00:23Z",        "livemode": false,        "appId": "5c1e9a47-2d3b-4f86-a0e9-7b4c2d18f365",        "accountId": null,        "subjectId": null,        "provider": null,        "data": {          "object": {            "message": "The sandbox inbox is ready."          }        },        "request": null      }    }  ],  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {    "nextCursor": null  }}

GET/v2/webhooks/listen

Stream sandbox webhook events

Opens a Server-Sent Events stream of the sandbox's events as they happen, so you can receive webhooks on a machine with no public endpoint. Requires a test key. The stream starts with an event: ready message that includes a signing secret created for this stream. Each event: delivery message that follows includes a body and the same headers as a real delivery, signed with that secret. POST each one to your local handler to run it exactly as in production. The pyas webhooks listen --forward command does this for you. A stream stays open for 1 hour, and an app can have at most 5 streams open at a time.

In the sandbox: The stream stays open. In a terminal, run curl -N -H "x-api-key: <test key>" https://api.pyas.io/v2/webhooks/listen, then create or update an event in the sandbox.

This endpoint streams events for as long as the connection stays open. Use the CLI (`pyas webhooks listen`) or cURL.

Query parameters

  • Name
    events
    Type
    string
    Description

    A comma-separated list of event types to relay. Accepts * (the default) or a family such as event.*.

Errors

Request

GET
/v2/webhooks/listen
curl 'https://api.pyas.io/v2/webhooks/listen?events=event.*%2Cconnection.*' \  -H "x-api-key: $PYAS_API_KEY"

Response

event: ready
data: {...}

event: delivery
data: {...}

POST/v2/webhooks/{webhookId}/replay

Replay failed webhook deliveries

Replays the deliveries to this endpoint that failed after every attempt, starting from the given time. Each request replays at most 1000 deliveries.

Sends the request to your sandbox, never to live data.

Path parameters

  • Name
    webhookId
    Type
    string
    Required
    required
    Description

Headers

  • Name
    Idempotency-Key
    Type
    string
    Description

    A unique key that makes the request safe to retry. A retry with the same key returns the original response and does not make the change twice. Keys are kept for 24 hours.

Body

  • Name
    since
    Type
    string
    Required
    required
    Description

    An ISO 8601 time. Replays failed deliveries created at or after this time.

Errors

Request

POST
/v2/webhooks/{webhookId}/replay
curl -X POST 'https://api.pyas.io/v2/webhooks/whk_sbx671d37d5e9995de7b6be/replay' \  -H "x-api-key: $PYAS_API_KEY" \  -H "content-type: application/json" \  -d '{  "since": "2026-11-13T00:00:00Z"}'

Response

202 Accepted
{  "success": true,  "data": {    "replayed": 0  },  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

GET/v2/webhooks/{webhookId}/secret

Retrieve the signing secret

Returns the secret that deliveries are signed with and, after a rotation, when the previous secret stops signing. Requires a key with the write scope.

Sends the request to your sandbox, never to live data.

Path parameters

  • Name
    webhookId
    Type
    string
    Required
    required
    Description

Errors

Request

GET
/v2/webhooks/{webhookId}/secret
curl 'https://api.pyas.io/v2/webhooks/whk_sbx671d37d5e9995de7b6be/secret' \  -H "x-api-key: $PYAS_API_KEY"

Response

200 OK
{  "success": true,  "data": {    "signingSecret": "whsec_example",    "previous": null  },  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

POST/v2/webhooks/{webhookId}/test

Send a test delivery

Sends a sample of any event type to the endpoint immediately, signed like a real delivery, and returns the result. The result includes your endpoint's status code, its response body, the time it took, and exactly what was sent, with your own credential hidden. Test deliveries are not retried, don't count against the endpoint, are marked "test": true, and are left out of the event list. You can test an endpoint while it is disabled.

In the sandbox: You can send any event type. Your handler receives a realistic sample, signed like a real delivery, and the response shows how your endpoint responded.

Sends the request to your sandbox, never to live data.

Path parameters

  • Name
    webhookId
    Type
    string
    Required
    required
    Description

Headers

  • Name
    Idempotency-Key
    Type
    string
    Description

    A unique key that makes the request safe to retry. A retry with the same key returns the original response and does not make the change twice. Keys are kept for 24 hours.

Body

  • Name
    type
    Type
    string
    Description

    The event type to send a sample of. Defaults to webhook.ping.

Errors

Request

POST
/v2/webhooks/{webhookId}/test
curl -X POST 'https://api.pyas.io/v2/webhooks/whk_sbx671d37d5e9995de7b6be/test' \  -H "x-api-key: $PYAS_API_KEY" \  -H "content-type: application/json" \  -d '{  "type": "event.updated"}'

Response

200 OK
{  "success": true,  "data": {    "delivered": true,    "delivery": {      "deliveryId": "del_v2dH4f5ag8CB77OT1WBx",      "webhookId": "whk_sbx671d37d5e9995de7b6be",      "eventId": "evt_7yChTM94C793nCcM9697",      "eventType": "event.updated",      "accountId": null,      "status": "delivered",      "attempts": [        {          "n": 1,          "at": "2026-11-14T20:00:25Z",          "httpStatus": 200,          "responseBody": "{\"received\":true,\"by\":\"the Pyas sandbox inbox\"}",          "responseTimeMs": 0,          "error": null        }      ],      "nextAttemptAt": null,      "replays": 0,      "createdAt": "2026-11-14T20:00:25Z",      "completedAt": "2026-11-14T20:00:25Z"    },    "sent": {      "url": "pyas://sandbox-inbox",      "headers": {        "Content-Type": "application/json",        "User-Agent": "Pyas-Webhooks/2",        "Pyas-Event-Id": "evt_7yChTM94C793nCcM9697",        "Pyas-Event-Type": "event.updated",        "Pyas-Delivery-Id": "del_v2dH4f5ag8CB77OT1WBx",        "Pyas-Timestamp": "1794168025",        "Pyas-Signature": "t=1794168025,v1=3c070b14da751b2be2c4d1a452b6e588894caeb651fbacdd8650f467dded1091"      },      "body": "{\"id\":\"evt_7yChTM94C793nCcM9697\",\"type\":\"event.updated\",\"apiVersion\":\"2\",\"createdAt\":\"2026-11-14T20:00:25Z\",\"livemode\":false,\"appId\":\"5c1e9a47-2d3b-4f86-a0e9-7b4c2d18f365\",\"accountId\":null,\"subjectId\":null,\"provider\":null,\"data\":{\"object\":{\"id\":\"sample1a2b3c4d5e6f7g8h9i\",\"calendarId\":\"primary\",\"seriesId\":null,\"isSeries\":false}},\"request\":null,\"test\":true}"    }  },  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

GET/v2/webhooks/deliveries/{deliveryId}

Get a webhook delivery

Returns one delivery, including every attempt.

Sends the request to your sandbox, never to live data.

Path parameters

  • Name
    deliveryId
    Type
    string
    Required
    required
    Description

Errors

Request

GET
/v2/webhooks/deliveries/{deliveryId}
curl 'https://api.pyas.io/v2/webhooks/deliveries/del_sbxf61f198fc5d9f0072a38' \  -H "x-api-key: $PYAS_API_KEY"

Response

200 OK
{  "success": true,  "data": {    "deliveryId": "del_sbxf61f198fc5d9f0072a38",    "webhookId": "whk_sbx671d37d5e9995de7b6be",    "eventId": "evt_sbx0b0f2dbfa1198fcb366c",    "eventType": "webhook.ping",    "accountId": null,    "status": "delivered",    "attempts": [      {        "n": 1,        "at": "2026-11-14T20:00:23Z",        "httpStatus": 200,        "responseBody": "{\"received\":true,\"by\":\"the Pyas sandbox inbox\"}",        "responseTimeMs": 0,        "error": null      }    ],    "nextAttemptAt": null,    "replays": 0,    "createdAt": "2026-11-14T20:00:23Z",    "completedAt": "2026-11-14T20:00:23Z"  },  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

GET/v2/webhooks/events/{eventId}

Get a webhook event

Returns one event and its payload, exactly as delivered.

Sends the request to your sandbox, never to live data.

Path parameters

  • Name
    eventId
    Type
    string
    Required
    required
    Description

Errors

Request

GET
/v2/webhooks/events/{eventId}
curl 'https://api.pyas.io/v2/webhooks/events/evt_sbx0b0f2dbfa1198fcb366c' \  -H "x-api-key: $PYAS_API_KEY"

Response

200 OK
{  "success": true,  "data": {    "eventId": "evt_sbx0b0f2dbfa1198fcb366c",    "type": "webhook.ping",    "accountId": null,    "subjectId": null,    "provider": null,    "createdAt": "2026-11-14T20:00:23Z",    "payload": {      "id": "evt_sbx0b0f2dbfa1198fcb366c",      "type": "webhook.ping",      "apiVersion": "2",      "createdAt": "2026-11-14T20:00:23Z",      "livemode": false,      "appId": "5c1e9a47-2d3b-4f86-a0e9-7b4c2d18f365",      "accountId": null,      "subjectId": null,      "provider": null,      "data": {        "object": {          "message": "The sandbox inbox is ready."        }      },      "request": null    }  },  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

POST/v2/webhooks/{webhookId}/secret/rotate

Rotate the signing secret

Creates a new signing secret. The previous secret keeps signing deliveries alongside the new one for graceHours (default 24, at most 168), so you can update your endpoint without rejecting any deliveries.

Sends the request to your sandbox, never to live data.

Path parameters

  • Name
    webhookId
    Type
    string
    Required
    required
    Description

Headers

  • Name
    Idempotency-Key
    Type
    string
    Description

    A unique key that makes the request safe to retry. A retry with the same key returns the original response and does not make the change twice. Keys are kept for 24 hours.

Body

  • Name
    graceHours
    Type
    integer
    Description

    From 0 to 168.

Errors

Request

POST
/v2/webhooks/{webhookId}/secret/rotate
curl -X POST 'https://api.pyas.io/v2/webhooks/whk_sbx671d37d5e9995de7b6be/secret/rotate' \  -H "x-api-key: $PYAS_API_KEY" \  -H "content-type: application/json" \  -d '{  "graceHours": 24}'

Response

200 OK
{  "success": true,  "data": {    "signingSecret": "whsec_example",    "previous": {      "expiresAt": "2026-11-15T20:00:25.724Z"    }  },  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}

POST/v2/webhooks/deliveries/{deliveryId}/replay

Replay a webhook delivery

Replays the delivery with a new series of attempts, sending the same event with the same body and Pyas-Event-Id. You can also replay a delivery that succeeded, for example if your endpoint lost it.

Sends the request to your sandbox, never to live data.

Path parameters

  • Name
    deliveryId
    Type
    string
    Required
    required
    Description

Headers

  • Name
    Idempotency-Key
    Type
    string
    Description

    A unique key that makes the request safe to retry. A retry with the same key returns the original response and does not make the change twice. Keys are kept for 24 hours.

Errors

Request

POST
/v2/webhooks/deliveries/{deliveryId}/replay
curl -X POST 'https://api.pyas.io/v2/webhooks/deliveries/del_sbxf61f198fc5d9f0072a38/replay' \  -H "x-api-key: $PYAS_API_KEY"

Response

202 Accepted
{  "success": true,  "data": {    "deliveryId": "del_sbxf61f198fc5d9f0072a38",    "webhookId": "whk_sbx671d37d5e9995de7b6be",    "eventId": "evt_sbx0b0f2dbfa1198fcb366c",    "eventType": "webhook.ping",    "accountId": null,    "status": "pending",    "attempts": [      {        "n": 1,        "at": "2026-11-14T20:00:24Z",        "httpStatus": 200,        "responseBody": "{\"received\":true,\"by\":\"the Pyas sandbox inbox\"}",        "responseTimeMs": 0,        "error": null      }    ],    "nextAttemptAt": "2026-11-14T20:00:25Z",    "replays": 1,    "createdAt": "2026-11-14T20:00:24Z",    "completedAt": null  },  "provider": null,  "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",  "meta": {}}