Using API v1? See what changes in v2, or the v1 docs
Webhooks
Receive events at your own endpoints through signed, retried and recorded deliveries. Includes replays, test deliveries and a relay for sandbox events.
The examples use IDs from a sample sandbox. Your sandbox has the same people, calendars and events with its own IDs, which GET /v2/sandbox returns. When you're signed in to the dashboard, Try it fills in your IDs and sends the request to your sandbox, never to live data.
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.
Errors
AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
curl 'https://api.pyas.io/v2/webhooks' \ -H "x-api-key: $PYAS_API_KEY"Response
{ "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": {}}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.
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
VALIDATION_INVALID_BODY(400): The request body is not valid.AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.BILLING_FEATURE_UNAVAILABLE(403): This feature is not included in the current plan.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_LIMIT_REACHED(409): The limit for this resource has been reached.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
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
{ "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 a webhook endpoint
Path parameters
- Name
webhookId- Type
- string
- Required
- required
- Description
Errors
AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_NOT_FOUND(404): The calendar or event was not found.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
curl 'https://api.pyas.io/v2/webhooks/whk_sbx671d37d5e9995de7b6be' \ -H "x-api-key: $PYAS_API_KEY"Response
{ "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": {}}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.
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
nullto remove it.
- Name
status- Type
- string
- Description
One of
active,disabled.
- Name
payloadVersion- Type
- string
- Description
One of
v1,v2.
Errors
VALIDATION_INVALID_BODY(400): The request body is not valid.AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_NOT_FOUND(404): The calendar or event was not found.VALIDATION_ENDPOINT_UNREACHABLE(422): The webhook endpoint did not accept a test delivery.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
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
{ "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 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.
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
AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_NOT_FOUND(404): The calendar or event was not found.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
curl -X DELETE 'https://api.pyas.io/v2/webhooks/whk_sbx671d37d5e9995de7b6be' \ -H "x-api-key: $PYAS_API_KEY"Response
{ "success": true, "data": null, "provider": null, "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d", "meta": {}}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.
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
nextCursorfrom the previous page.
Errors
AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
curl 'https://api.pyas.io/v2/webhooks/deliveries?webhookId=whk_sbx671d37d5e9995de7b6be' \ -H "x-api-key: $PYAS_API_KEY"Response
{ "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 }}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.
Errors
AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
curl 'https://api.pyas.io/v2/webhooks/event-types' \ -H "x-api-key: $PYAS_API_KEY"Response
{ "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": {}}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.
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
nextCursorfrom the previous page.
Errors
AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
curl 'https://api.pyas.io/v2/webhooks/events?type=webhook.ping' \ -H "x-api-key: $PYAS_API_KEY"Response
{ "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 }}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 asevent.*.
Errors
AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_SANDBOX_MISMATCH(403): A test key was used with live data, or a live key was used with sandbox data.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_LIMIT_REACHED(409): The limit for this resource has been reached.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
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: {...}
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.
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
VALIDATION_INVALID_BODY(400): The request body is not valid.AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_NOT_FOUND(404): The calendar or event was not found.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
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
{ "success": true, "data": { "replayed": 0 }, "provider": null, "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d", "meta": {}}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.
Path parameters
- Name
webhookId- Type
- string
- Required
- required
- Description
Errors
VALIDATION_INVALID_BODY(400): The request body is not valid.AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_NOT_FOUND(404): The calendar or event was not found.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
curl 'https://api.pyas.io/v2/webhooks/whk_sbx671d37d5e9995de7b6be/secret' \ -H "x-api-key: $PYAS_API_KEY"Response
{ "success": true, "data": { "signingSecret": "whsec_example", "previous": null }, "provider": null, "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d", "meta": {}}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.
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
VALIDATION_INVALID_BODY(400): The request body is not valid.AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_NOT_FOUND(404): The calendar or event was not found.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
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
{ "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 a webhook delivery
Returns one delivery, including every attempt.
Path parameters
- Name
deliveryId- Type
- string
- Required
- required
- Description
Errors
AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_NOT_FOUND(404): The calendar or event was not found.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
curl 'https://api.pyas.io/v2/webhooks/deliveries/del_sbxf61f198fc5d9f0072a38' \ -H "x-api-key: $PYAS_API_KEY"Response
{ "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 a webhook event
Returns one event and its payload, exactly as delivered.
Path parameters
- Name
eventId- Type
- string
- Required
- required
- Description
Errors
AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_NOT_FOUND(404): The calendar or event was not found.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
curl 'https://api.pyas.io/v2/webhooks/events/evt_sbx0b0f2dbfa1198fcb366c' \ -H "x-api-key: $PYAS_API_KEY"Response
{ "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": {}}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.
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
VALIDATION_INVALID_BODY(400): The request body is not valid.AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_NOT_FOUND(404): The calendar or event was not found.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
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
{ "success": true, "data": { "signingSecret": "whsec_example", "previous": { "expiresAt": "2026-11-15T20:00:25.724Z" } }, "provider": null, "traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d", "meta": {}}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.
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
AUTH_INVALID_KEY(401): The API key is missing, unknown or revoked.AUTH_EXPIRED_KEY(401): The API key has expired.AUTH_INSUFFICIENT_SCOPE(403): The API key or session does not have permission to make this request.RESOURCE_NOT_FOUND(404): The calendar or event was not found.RESOURCE_CONFLICT(409): The resource was modified or already exists.RATE_LIMIT_EXCEEDED(429): The request rate exceeds this plan's rate limit.RATE_LIMIT_BURST(429): Too many requests were sent in a short period.INTERNAL_ERROR(500): Something went wrong on our end.
Request
curl -X POST 'https://api.pyas.io/v2/webhooks/deliveries/del_sbxf61f198fc5d9f0072a38/replay' \ -H "x-api-key: $PYAS_API_KEY"Response
{ "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": {}}