Webhooks

List webhook deliveries

Lists delivery attempts for one endpoint, newest first by createdAt.

GET/webhooks/{id}/deliveries

Lists delivery attempts for one endpoint, newest first by createdAt. This is the log you read when a receiver stops seeing events.

A delivery moves pending -> processing -> succeeded or failed. attempts counts tries so far, up to 5; nextAttemptAt is when the next retry is due (backoff 30s, 2m, 10m, 30m, 1h). responseStatus and error record what the receiver said. Anything outside 2xx, a 3xx redirect, or a timeout past 10 seconds is a failure.

Response bodies are omitted from this list. Fetch a single delivery with GET /webhooks/{id}/deliveries/{deliveryId} to see responseBody, which is truncated at 4000 characters.

Several deliveries can share one eventId when an event fans out to multiple endpoints, and a redelivery creates a new delivery row reusing the original eventId, so eventId is not unique. De-duplicate on it in your handler.

Refused for project-restricted keys.

Requires the webhooks:read scope.

curl --request GET \
  --url 'https://api.envless.cloud/webhooks/<id>/deliveries' \
  --header 'Authorization: Bearer ev_sk_xxxxxxxxxxxx'
Try itGEThttps://api.envless.cloud/webhooks/{id}/deliveries
Path parameters
Query parameters

Authorization

  • Authorizationstringrequiredheader

    Bearer token: an Envless API key (ev_sk_…), created in the dashboard or with POST /keys. The key is bound to one workspace; every request runs inside it.

Path parameters

  • idstringrequireduuid

    The endpoint's id (UUIDv7). Only deliveries belonging to this endpoint are returned.

Query parameters

  • statusenum<string>

    Filter by delivery status. Any other value is a 400, not an empty page.

    Options: "pending", "processing", "succeeded", "failed"

  • limitintegermin 1max 100

    Page size. Must be a whole number between 1 and 100.

    Default: 25

  • offsetintegermin 0

    Number of deliveries to skip. Must be a whole number of 0 or greater.

    Default: 0

Responses

200OK

List webhook deliveries succeeded.

  • successbooleanrequired
  • codeintegerrequired

    The HTTP status of this response, repeated in the body.

  • messagestringrequired
  • errornullrequired
  • dataobjectrequired
    Show child attributesHide child attributes
    • deliveriesobject[]required
      Show child attributesHide child attributes
      • idstringrequired
      • webhookIdstringrequired
      • eventIdstringrequired
      • eventTypestringrequired
      • statusstringrequired
      • attemptsinteger
      • responseStatusinteger | null
      • errorstring | null
      • lastAttemptAtstring | nulldate-time
      • nextAttemptAtstring | nulldate-time
      • succeededAtstring | nulldate-time
      • failedAtstring | nulldate-time
      • createdAtstringrequireddate-time
    • paginationobjectrequired
      Show child attributesHide child attributes
      • limitintegerrequired
      • offsetintegerrequired
      • totalCountintegerrequired
      • hasMorebooleanrequired
  • _reqobject
    Show child attributesHide child attributes
    • reqIdstring
    • resTimeinteger | null
{
    "success": true,
    "code": 200,
    "message": "OK",
    "error": null,
    "data": {
        "deliveries": [
            {
                "id": "<string>",
                "webhookId": "<string>",
                "eventId": "<string>",
                "eventType": "<string>",
                "status": "<string>",
                "attempts": 1,
                "responseStatus": 1,
                "error": "<string>",
                "lastAttemptAt": "2026-05-30T10:14:22.000Z",
                "nextAttemptAt": "2026-05-30T10:14:22.000Z",
                "succeededAt": "2026-05-30T10:14:22.000Z",
                "failedAt": "2026-05-30T10:14:22.000Z",
                "createdAt": "2026-05-30T10:14:22.000Z"
            }
        ],
        "pagination": {
            "limit": 1,
            "offset": 1,
            "totalCount": 1,
            "hasMore": true
        }
    },
    "_req": {
        "reqId": "<string>",
        "resTime": 1
    }
}

Error envelope

Every failure below returns the same shape with success: false; branch on error.code, never on message.

  • successbooleanrequired
  • codeintegerrequired

    The HTTP status of this response, repeated in the body.

  • messagestringrequired
  • errorobjectrequired

    Machine-readable failure detail. Null on success.

    Show child attributesHide child attributes
    • codeenum<string>required

      Stable error code. Branch on this, never on message.

      Options: "auth.api_key_required", "auth.api_key_not_found", "auth.api_key_user_missing", "auth.api_key_owner_not_member", "auth.scope_missing", "auth.scope_ceiling_exceeded", "auth.key_project_restricted", "auth.product_restricted", "auth.permission_denied", "auth.self_mutation_forbidden", "auth.step_up_required", "auth.step_up_fresh_required", "auth.cli_token_not_accepted", "auth.two_factor_required", "auth.workspace_two_factor_required", "auth.two_factor_invalid", "auth.two_factor_replayed", "auth.two_factor_locked", "auth.two_factor_not_enabled", "auth.two_factor_already_enabled", "request.body_must_be_json", "request.body_invalid", "request.body_too_large", "request.rate_limited", "request.invalid_pagination", "workspace.not_found", "workspace.slug_conflict", "workspace.limit_reached", "workspace.delete_last", "workspace.delete_too_early", "workspace.delete_has_projects", "domain.not_configured", "domain.not_found", "domain.invalid", "domain.taken", "domain.attempt_limit", "product.not_found", "product.slug_conflict", "product.locked", "project.not_found", "project.ambiguous_product", "project.slug_conflict", "environment.not_found", "environment.slug_conflict", "environment.expose_disabled", "environment.expose_private_product", "variable.not_found", "variable.name_conflict", "variable.ciphertext_invalid", "variable.baseline_drifted", "version.not_found", "member.not_found", "member.already_exists", "member.self_invite", "member.self_mutation", "member.owner_immutable", "member.owner_cannot_leave", "member.seat_limit", "invite.already_pending", "role.owner_not_assignable", "role.limit_reached", "role.immutable", "role.in_use", "invite.not_found", "invite.expired", "role.not_found", "role.name_conflict", "key.not_found", "key.limit_reached", "webhook.not_found", "webhook.delivery_not_found", "plan.limit_reached", "plan.upgrade_required", "request.not_found", "request.method_not_allowed", "request.conflict", "auth.unauthorized", "auth.forbidden", "request.invalid", "server.error"

    • resourcestring

      The resource or scope the failure relates to.

    • fieldstring

      The request field that caused the failure.

  • dataobject | nullrequired

    Null on most failures. A plan limit carries { limit, current }.

  • _reqobject
    Show child attributesHide child attributes
    • reqIdstring
    • resTimeinteger | null
{
    "success": false,
    "code": 400,
    "message": "Something went wrong.",
    "error": {
        "code": "auth.api_key_required",
        "resource": "<string>",
        "field": "<string>"
    },
    "data": {},
    "_req": {
        "reqId": "<string>",
        "resTime": 1
    }
}

400Bad Request

  • request.body_invalid - status is not one of pending, processing, succeeded, failed (error.field is status).
  • request.invalid_pagination - limit or offset is not a whole number, or limit is outside 1-100.

401Unauthorized

  • auth.api_key_required - the Authorization header is missing, or its value does not start with ev_sk_.
  • auth.api_key_not_found - the key is unknown, inactive or revoked.
  • auth.api_key_user_missing - the key is valid but its owner no longer exists.
  • auth.api_key_owner_not_member - the key's owner is no longer a member of the key's workspace.

403Forbidden

  • auth.key_project_restricted - the calling key is fenced to specific projects.
  • auth.workspace_two_factor_required - the workspace requires two-factor authentication and the key's owner has not turned it on.
  • auth.scope_missing - the key lacks a scope this operation requires; error.resource names it.

404Not Found

webhook.not_found - no live endpoint with that id exists in this workspace.

500Internal Server Error

server.error - an unexpected error; retry with backoff.

503Service Unavailable

server.error - the API is in maintenance mode or is missing its configuration; retry with backoff.