Docs menu

Webhooks API reference

Signed HTTPS callbacks when generations and sets finish.

View as Markdown

List webhooks

GET /webhooks

Auth: API key with scope webhooks:read or webhooks:write, or access token.

Requires webhooks:read or webhooks:write. An API key sees the webhooks of its own environment; an access token sees both (or one with ?environment=). Secrets are never returned.

Credits. Free: no credits.

Parameters

Name In Type Description
environment query ApiKeyEnvironment

Responses

200 Webhooks, newest first.

Schema WebhookList (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Example 200 response
{
  "data": [
    {
      "id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
      "environment": "live",
      "url": "https://hooks.example.com/…shot",
      "events": [],
      "description": "string",
      "enabled": true,
      "creditsLowThreshold": 0,
      "secret": {
        "hint": "whsec_…Xy7Q",
        "rotatedAt": "2026-10-21T12:00:00.000Z",
        "previousExpiresAt": "2026-10-21T12:00:00.000Z"
      },
      "createdAt": "2026-10-21T12:00:00.000Z",
      "updatedAt": "2026-10-21T12:00:00.000Z"
    }
  ]
}
400 INVALID_INPUT. A credential sent in the query string is refused before authentication, without RateLimit headers.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

401 UNAUTHORIZED: missing, malformed, unknown, revoked or expired credential.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id

403 FORBIDDEN: see details.reason.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

429 RATE_LIMITED: the request rate of the API key (details.scope key) or of the account (user) is exhausted for this window, or too many failed authentication attempts came from this IP (no details.scope and no RateLimit headers). Retry after Retry-After seconds.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy , Retry-After

default Error (see ErrorEnvelope and /v1/docs/errors).

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Create a webhook

POST /webhooks

Auth: API key with scope webhooks:write, or access token.

Requires webhooks:write (not a default scope). Registers an https:// endpoint (port 443, public addresses only) for the given events of one environment; at most 10 per environment. The signing secret is in this response only.

When to use. Instead of polling, when you run an https endpoint that can receive callbacks; agents in a terminal usually poll.

Credits. Free: no credits.

Request body application/json

Required. Schema CreateWebhookRequest.

FieldTypeDescription
urlrequiredstringAn https:// URL on port 443, without credentials, at most 2048 characters, whose host resolves only to public addresses (checked now and before every delivery). Shown masked afterwards.
eventsrequiredarray of WebhookEventTypeThe business events to deliver (1–4, unique).
descriptionstringFree text, at most 200 characters.
enabledbooleanDefault true.
creditsLowThresholdintegerDefault 5.
environmentApiKeyEnvironmentWhich events the webhook receives: live or test (test-key generations and sets). Default: the API key environment (access tokens: live). An API key can only create webhooks of its own environment.
Example request body
{
  "url": "https://hooks.example.com/storeshot",
  "events": [
    "generation.succeeded"
  ],
  "description": "string",
  "enabled": true,
  "creditsLowThreshold": 1,
  "environment": "live"
}

Responses

201 Created. Store secret.value now; it is never shown again.

Schema WebhookWithSecret (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Example 201 response
{
  "id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
  "environment": "live",
  "url": "https://hooks.example.com/…shot",
  "events": [
    "generation.succeeded"
  ],
  "description": "string",
  "enabled": true,
  "creditsLowThreshold": 0,
  "secret": {
    "hint": "whsec_…Xy7Q",
    "rotatedAt": "2026-10-21T12:00:00.000Z",
    "previousExpiresAt": "2026-10-21T12:00:00.000Z",
    "value": "string"
  },
  "createdAt": "2026-10-21T12:00:00.000Z",
  "updatedAt": "2026-10-21T12:00:00.000Z"
}
400 INVALID_INPUT. A credential sent in the query string is refused before authentication, without RateLimit headers.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

401 UNAUTHORIZED: missing, malformed, unknown, revoked or expired credential.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id

403 FORBIDDEN: see details.reason.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

413 PAYLOAD_TOO_LARGE.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id

422 WEBHOOK_URL_REJECTED: the host is not public or does not resolve (details.reason).

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

429 RATE_LIMITED: the request rate of the API key (details.scope key) or of the account (user) is exhausted for this window, or too many failed authentication attempts came from this IP (no details.scope and no RateLimit headers). Retry after Retry-After seconds.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy , Retry-After

503 SERVICE_UNAVAILABLE: secrets cannot be issued right now.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

default Error (see ErrorEnvelope and /v1/docs/errors).

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Get a webhook

GET /webhooks/{id}

Auth: API key with scope webhooks:read or webhooks:write, or access token.

Requires webhooks:read or webhooks:write. The secret is never returned.

Credits. Free: no credits.

Parameters

Name In Type Description
id required path string Webhook id. An unknown or malformed id is 404.

Responses

200 The webhook.

Schema Webhook (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Example 200 response
{
  "id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
  "environment": "live",
  "url": "https://hooks.example.com/…shot",
  "events": [
    "generation.succeeded"
  ],
  "description": "string",
  "enabled": true,
  "creditsLowThreshold": 0,
  "secret": {
    "hint": "whsec_…Xy7Q",
    "rotatedAt": "2026-10-21T12:00:00.000Z",
    "previousExpiresAt": "2026-10-21T12:00:00.000Z"
  },
  "createdAt": "2026-10-21T12:00:00.000Z",
  "updatedAt": "2026-10-21T12:00:00.000Z"
}
400 INVALID_INPUT. A credential sent in the query string is refused before authentication, without RateLimit headers.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

401 UNAUTHORIZED: missing, malformed, unknown, revoked or expired credential.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id

403 FORBIDDEN: see details.reason.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

404 NOT_FOUND: no such resource for this caller.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

429 RATE_LIMITED: the request rate of the API key (details.scope key) or of the account (user) is exhausted for this window, or too many failed authentication attempts came from this IP (no details.scope and no RateLimit headers). Retry after Retry-After seconds.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy , Retry-After

default Error (see ErrorEnvelope and /v1/docs/errors).

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Delete a webhook

DELETE /webhooks/{id}

Auth: API key with scope webhooks:write, or access token.

Requires webhooks:write. Deletes the webhook with its delivery log; pending deliveries are dropped.

Credits. Free: no credits.

Parameters

Name In Type Description
id required path string Webhook id. An unknown or malformed id is 404.

Responses

204 Deleted.

Headers: X-Request-Id , RateLimit , RateLimit-Policy

400 INVALID_INPUT. A credential sent in the query string is refused before authentication, without RateLimit headers.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

401 UNAUTHORIZED: missing, malformed, unknown, revoked or expired credential.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id

403 FORBIDDEN: see details.reason.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

404 NOT_FOUND: no such resource for this caller.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

429 RATE_LIMITED: the request rate of the API key (details.scope key) or of the account (user) is exhausted for this window, or too many failed authentication attempts came from this IP (no details.scope and no RateLimit headers). Retry after Retry-After seconds.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy , Retry-After

default Error (see ErrorEnvelope and /v1/docs/errors).

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Update a webhook

PATCH /webhooks/{id}

Auth: API key with scope webhooks:write, or access token.

Requires webhooks:write. Send any of url, events, description, enabled, creditsLowThreshold. A new url is checked like on create. While a webhook is disabled it gets no new events, and a pending delivery reaching its next attempt is canceled.

Credits. Free: no credits.

Parameters

Name In Type Description
id required path string Webhook id. An unknown or malformed id is 404.

Request body application/json

Required. Schema UpdateWebhookRequest.

FieldTypeDescription
urlstringAn https:// URL on port 443, without credentials, at most 2048 characters, whose host resolves only to public addresses (checked now and before every delivery). Shown masked afterwards.
eventsarray of WebhookEventTypeThe business events to deliver (1–4, unique).
descriptionstringFree text, at most 200 characters.
enabledboolean
creditsLowThresholdintegercredits.low fires when the live balance falls below this many credits (1–10000).
Example request body
{
  "url": "https://hooks.example.com/storeshot",
  "events": [
    "generation.succeeded"
  ],
  "description": "string",
  "enabled": true,
  "creditsLowThreshold": 1
}

Responses

200 The updated webhook.

Schema Webhook (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Example 200 response
{
  "id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
  "environment": "live",
  "url": "https://hooks.example.com/…shot",
  "events": [
    "generation.succeeded"
  ],
  "description": "string",
  "enabled": true,
  "creditsLowThreshold": 0,
  "secret": {
    "hint": "whsec_…Xy7Q",
    "rotatedAt": "2026-10-21T12:00:00.000Z",
    "previousExpiresAt": "2026-10-21T12:00:00.000Z"
  },
  "createdAt": "2026-10-21T12:00:00.000Z",
  "updatedAt": "2026-10-21T12:00:00.000Z"
}
400 INVALID_INPUT. A credential sent in the query string is refused before authentication, without RateLimit headers.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

401 UNAUTHORIZED: missing, malformed, unknown, revoked or expired credential.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id

403 FORBIDDEN: see details.reason.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

404 NOT_FOUND: no such resource for this caller.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

413 PAYLOAD_TOO_LARGE.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id

422 WEBHOOK_URL_REJECTED: the host is not public or does not resolve (details.reason).

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

429 RATE_LIMITED: the request rate of the API key (details.scope key) or of the account (user) is exhausted for this window, or too many failed authentication attempts came from this IP (no details.scope and no RateLimit headers). Retry after Retry-After seconds.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy , Retry-After

default Error (see ErrorEnvelope and /v1/docs/errors).

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Rotate the signing secret

POST /webhooks/{id}/rotate-secret

Auth: API key with scope webhooks:write, or access token.

Requires webhooks:write. Issues a new secret (in this response only). For gracePeriodSeconds (default 24 hours) deliveries carry a signature with each secret, so receivers can switch without dropping events.

Credits. Free: no credits.

Parameters

Name In Type Description
id required path string Webhook id. An unknown or malformed id is 404.

Request body application/json

Optional. Schema RotateWebhookSecretRequest.

FieldTypeDescription
gracePeriodSecondsintegerHow long the previous secret keeps signing deliveries next to the new one (0–604800, default 86400). 0 drops it at once.
Example request body
{
  "gracePeriodSeconds": 0
}

Responses

200 Rotated. Store secret.value now; it is never shown again.

Schema WebhookWithSecret (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Example 200 response
{
  "id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
  "environment": "live",
  "url": "https://hooks.example.com/…shot",
  "events": [
    "generation.succeeded"
  ],
  "description": "string",
  "enabled": true,
  "creditsLowThreshold": 0,
  "secret": {
    "hint": "whsec_…Xy7Q",
    "rotatedAt": "2026-10-21T12:00:00.000Z",
    "previousExpiresAt": "2026-10-21T12:00:00.000Z",
    "value": "string"
  },
  "createdAt": "2026-10-21T12:00:00.000Z",
  "updatedAt": "2026-10-21T12:00:00.000Z"
}
400 INVALID_INPUT. A credential sent in the query string is refused before authentication, without RateLimit headers.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

401 UNAUTHORIZED: missing, malformed, unknown, revoked or expired credential.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id

403 FORBIDDEN: see details.reason.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

404 NOT_FOUND: no such resource for this caller.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

413 PAYLOAD_TOO_LARGE.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id

429 RATE_LIMITED: the request rate of the API key (details.scope key) or of the account (user) is exhausted for this window, or too many failed authentication attempts came from this IP (no details.scope and no RateLimit headers). Retry after Retry-After seconds.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy , Retry-After

503 SERVICE_UNAVAILABLE: secrets cannot be issued right now.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

default Error (see ErrorEnvelope and /v1/docs/errors).

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Send a test event

POST /webhooks/{id}/test

Auth: API key with scope webhooks:write, or access token.

Requires webhooks:write. Queues a signed webhook.test event to this webhook (whatever its events); follow it in GET /webhooks/{id}/deliveries. At most 10 per webhook per hour.

Credits. Free: no credits.

Parameters

Name In Type Description
id required path string Webhook id. An unknown or malformed id is 404.

Responses

202 Queued.

Schema WebhookTestResult (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Example 202 response
{
  "eventId": "evt_3f2a…",
  "deliveryId": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d"
}
400 INVALID_INPUT. A credential sent in the query string is refused before authentication, without RateLimit headers.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

401 UNAUTHORIZED: missing, malformed, unknown, revoked or expired credential.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id

403 FORBIDDEN: see details.reason.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

404 NOT_FOUND: no such resource for this caller.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

409 WEBHOOK_STATE_CONFLICT: the webhook is disabled (details.action test).

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

429 RATE_LIMITED: the request rate of the API key (details.scope key) or of the account (user) is exhausted for this window, or too many failed authentication attempts came from this IP (no details.scope and no RateLimit headers). Retry after Retry-After seconds.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy , Retry-After

default Error (see ErrorEnvelope and /v1/docs/errors).

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

List deliveries

GET /webhooks/{id}/deliveries

Auth: API key with scope webhooks:read or webhooks:write, or access token.

Requires webhooks:read or webhooks:write. Deliveries of this webhook, newest first, with every attempt (status code, latency, error class and the start of the response body). Kept 30 days after they finish.

Credits. Free: no credits.

Parameters

Name In Type Description
id required path string Webhook id. An unknown or malformed id is 404.
limit query integer 1–100, default 20. Default: 20
cursor query string nextCursor of the previous page.
status query WebhookDeliveryStatus

Responses

200 A page of deliveries.

Schema WebhookDeliveryList (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Example 200 response
{
  "data": [
    {
      "id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
      "eventId": "evt_3f2a…",
      "eventType": "generation.succeeded",
      "status": "pending",
      "attempts": 0,
      "nextAttemptAt": "2026-10-21T12:00:00.000Z",
      "createdAt": "2026-10-21T12:00:00.000Z",
      "finishedAt": "2026-10-21T12:00:00.000Z",
      "log": [
        {}
      ]
    }
  ],
  "nextCursor": "string"
}
400 INVALID_INPUT. A credential sent in the query string is refused before authentication, without RateLimit headers.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

401 UNAUTHORIZED: missing, malformed, unknown, revoked or expired credential.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id

403 FORBIDDEN: see details.reason.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

404 NOT_FOUND: no such resource for this caller.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

429 RATE_LIMITED: the request rate of the API key (details.scope key) or of the account (user) is exhausted for this window, or too many failed authentication attempts came from this IP (no details.scope and no RateLimit headers). Retry after Retry-After seconds.

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy , Retry-After

default Error (see ErrorEnvelope and /v1/docs/errors).

Schema ErrorEnvelope (application/json).

Headers: X-Request-Id , RateLimit , RateLimit-Policy

Webhook events

Sent as signed POST requests to your webhook URL. See the webhook endpoints above to subscribe.

generation.succeeded: A generation succeeded

Sent as a signed POST to your webhook URL (at least once, unordered). Answer any 2xx within 10 seconds; anything else is retried 7 times over about 23 hours. Verify Storeshot-Signature over the raw body before parsing it.

Headers: storeshot-signature , storeshot-event-id , storeshot-event-type , storeshot-delivery-id , storeshot-delivery-attempt

Payload schema GenerationSucceededEvent.

FieldTypeDescription
idrequiredstringEvent id (evt_…), identical on every attempt: deduplicate on it.
typerequiredstringOne of: "generation.succeeded"
createdrequiredstringRFC 3339 UTC timestamp.
environmentrequiredApiKeyEnvironment
apiVersionrequiredstring
datarequiredWebhookGenerationData
Example generation.succeeded payload
{
  "id": "string",
  "type": "generation.succeeded",
  "created": "2026-10-21T12:00:00.000Z",
  "environment": "live",
  "apiVersion": "1.8.0",
  "data": {
    "id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
    "status": "queued",
    "environment": "live",
    "inputs": [
      {
        "uploadId": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
        "sourceUrl": "string"
      }
    ],
    "store": "app_store",
    "device": "app_store_1290x2796",
    "size": {
      "width": 1,
      "height": 1
    },
    "orientation": "portrait",
    "app": {
      "name": "string",
      "description": "string"
    },
    "caption": "string",
    "palette": [
      {}
    ],
    "logo": {
      "uploadId": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d"
    },
    "metadata": {},
    "creditsCharged": 0,
    "creditsRefunded": 0,
    "result": {
      "image": {
        "contentType": "image/png",
        "width": 1290,
        "height": 2796
      },
      "thumbnail": {
        "contentType": "image/webp"
      },
      "caption": "string",
      "palette": []
    },
    "error": {
      "code": "GENERATION_FAILED",
      "message": "string"
    },
    "createdAt": "2026-10-21T12:00:00.000Z",
    "startedAt": "2026-10-21T12:00:00.000Z",
    "finishedAt": "2026-10-21T12:00:00.000Z",
    "deletedAt": "2026-10-21T12:00:00.000Z",
    "screenshotSet": {
      "id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
      "position": 1,
…

Shortened; the Markdown version has the full example.

generation.failed: A generation failed or was canceled

Sent as a signed POST to your webhook URL (at least once, unordered). Answer any 2xx within 10 seconds; anything else is retried 7 times over about 23 hours. Verify Storeshot-Signature over the raw body before parsing it.

Headers: storeshot-signature , storeshot-event-id , storeshot-event-type , storeshot-delivery-id , storeshot-delivery-attempt

Payload schema GenerationFailedEvent.

FieldTypeDescription
idrequiredstringEvent id (evt_…), identical on every attempt: deduplicate on it.
typerequiredstringOne of: "generation.failed"
createdrequiredstringRFC 3339 UTC timestamp.
environmentrequiredApiKeyEnvironment
apiVersionrequiredstring
datarequiredWebhookGenerationData
Example generation.failed payload
{
  "id": "string",
  "type": "generation.failed",
  "created": "2026-10-21T12:00:00.000Z",
  "environment": "live",
  "apiVersion": "1.8.0",
  "data": {
    "id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
    "status": "queued",
    "environment": "live",
    "inputs": [
      {
        "uploadId": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
        "sourceUrl": "string"
      }
    ],
    "store": "app_store",
    "device": "app_store_1290x2796",
    "size": {
      "width": 1,
      "height": 1
    },
    "orientation": "portrait",
    "app": {
      "name": "string",
      "description": "string"
    },
    "caption": "string",
    "palette": [
      {}
    ],
    "logo": {
      "uploadId": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d"
    },
    "metadata": {},
    "creditsCharged": 0,
    "creditsRefunded": 0,
    "result": {
      "image": {
        "contentType": "image/png",
        "width": 1290,
        "height": 2796
      },
      "thumbnail": {
        "contentType": "image/webp"
      },
      "caption": "string",
      "palette": []
    },
    "error": {
      "code": "GENERATION_FAILED",
      "message": "string"
    },
    "createdAt": "2026-10-21T12:00:00.000Z",
    "startedAt": "2026-10-21T12:00:00.000Z",
    "finishedAt": "2026-10-21T12:00:00.000Z",
    "deletedAt": "2026-10-21T12:00:00.000Z",
    "screenshotSet": {
      "id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
      "position": 1,
…

Shortened; the Markdown version has the full example.

screenshot_set.completed: A screenshot set reached a final status

Sent as a signed POST to your webhook URL (at least once, unordered). Answer any 2xx within 10 seconds; anything else is retried 7 times over about 23 hours. Verify Storeshot-Signature over the raw body before parsing it.

Headers: storeshot-signature , storeshot-event-id , storeshot-event-type , storeshot-delivery-id , storeshot-delivery-attempt

Payload schema ScreenshotSetCompletedEvent.

FieldTypeDescription
idrequiredstringEvent id (evt_…), identical on every attempt: deduplicate on it.
typerequiredstringOne of: "screenshot_set.completed"
createdrequiredstringRFC 3339 UTC timestamp.
environmentrequiredApiKeyEnvironment
apiVersionrequiredstring
datarequiredWebhookScreenshotSetData
Example screenshot_set.completed payload
{
  "id": "string",
  "type": "screenshot_set.completed",
  "created": "2026-10-21T12:00:00.000Z",
  "environment": "live",
  "apiVersion": "1.8.0",
  "data": {
    "id": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
    "status": "queued",
    "environment": "live",
    "app": {
      "name": "string",
      "description": "string"
    },
    "orientation": "portrait",
    "palette": [
      {}
    ],
    "paletteSource": "given",
    "logo": {
      "uploadId": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d"
    },
    "metadata": {},
    "screens": [
      {
        "position": 1,
        "input": {},
        "caption": "string"
      }
    ],
    "stores": [
      {
        "devices": [],
        "featureGraphic": {}
      }
    ],
    "images": 1,
    "counts": {
      "queued": 0,
      "running": 0,
      "succeeded": 0,
      "failed": 0,
      "canceled": 0
    },
    "creditsCharged": 0,
    "creditsRefunded": 0,
    "children": [
      {
        "position": 1,
        "device": "string",
        "size": {},
        "featureGraphic": true,
        "generationId": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
        "attempt": 1,
        "error": {}
      }
    ],
    "archiveUrl": "/v1/screenshot-sets/5c1e…/archive",
    "createdAt": "2026-10-21T12:00:00.000Z",
    "updatedAt": "2026-10-21T12:00:00.000Z",
…

Shortened; the Markdown version has the full example.

credits.low: The live credit balance fell below the threshold

Sent as a signed POST to your webhook URL (at least once, unordered). Answer any 2xx within 10 seconds; anything else is retried 7 times over about 23 hours. Verify Storeshot-Signature over the raw body before parsing it.

Headers: storeshot-signature , storeshot-event-id , storeshot-event-type , storeshot-delivery-id , storeshot-delivery-attempt

Payload schema CreditsLowEvent.

FieldTypeDescription
idrequiredstringEvent id (evt_…), identical on every attempt: deduplicate on it.
typerequiredstringOne of: "credits.low"
createdrequiredstringRFC 3339 UTC timestamp.
environmentrequiredApiKeyEnvironment
apiVersionrequiredstring
datarequiredobject
data.balancesrequiredobject
data.balances.starterrequiredinteger
data.balances.subscriptionrequiredinteger
data.balances.purchasedrequiredinteger
data.totalrequiredinteger
data.thresholdrequiredinteger
Example credits.low payload
{
  "id": "string",
  "type": "credits.low",
  "created": "2026-10-21T12:00:00.000Z",
  "environment": "live",
  "apiVersion": "1.8.0",
  "data": {
    "balances": {
      "starter": 0,
      "subscription": 0,
      "purchased": 0
    },
    "total": 0,
    "threshold": 0
  }
}

webhook.test: Test event (POST /webhooks/{id}/test)

Sent as a signed POST to your webhook URL (at least once, unordered). Answer any 2xx within 10 seconds; anything else is retried 7 times over about 23 hours. Verify Storeshot-Signature over the raw body before parsing it.

Headers: storeshot-signature , storeshot-event-id , storeshot-event-type , storeshot-delivery-id , storeshot-delivery-attempt

Payload schema WebhookTestEvent.

FieldTypeDescription
idrequiredstringEvent id (evt_…), identical on every attempt: deduplicate on it.
typerequiredstringOne of: "webhook.test"
createdrequiredstringRFC 3339 UTC timestamp.
environmentrequiredApiKeyEnvironment
apiVersionrequiredstring
datarequiredobject
data.webhookIdrequiredstringFormat: uuid
data.messagerequiredstring
Example webhook.test payload
{
  "id": "string",
  "type": "webhook.test",
  "created": "2026-10-21T12:00:00.000Z",
  "environment": "live",
  "apiVersion": "1.8.0",
  "data": {
    "webhookId": "7d0e1a52-3c4b-4f6e-9a8d-2b1c0f9e8a7d",
    "message": "string"
  }
}