{"openapi":"3.0.0","paths":{"/v1/workspaces/{workspace_id}/webhooks/event-types":{"get":{"description":"Each type with a plain description, its group and an example `data` object (what \"Send test\" delivers).","operationId":"WebhooksController_trawl_workspace_listEventTypes","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWebhookEventTypesResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.webhook.read` (reads) or `trawl.webhook.manage` (changes, Send test, replay), or Trawl is not enabled for the workspace (`SERVICE_NOT_ENABLED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the events an endpoint can subscribe to.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.eventTypes","method":"list","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/endpoints":{"get":{"description":"At most 10, so this is always one page. Signing secrets are never returned here, only their last 4 characters.","operationId":"WebhooksController_trawl_workspace_listEndpoints","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWebhookEndpointsResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"The scope id in the path is malformed.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.webhook.read` (reads) or `trawl.webhook.manage` (changes, Send test, replay), or Trawl is not enabled for the workspace (`SERVICE_NOT_ENABLED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List webhook endpoints, oldest first.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"list","bind":["workspace_id"],"paginated":true}},"post":{"description":"Deliveries are signed with the Standard Webhooks headers `webhook-id`, `webhook-timestamp` and `webhook-signature` (`v1,<base64 HMAC-SHA256 of \"<id>.<timestamp>.<body>\">`); the returned `whsec_…` secret works unchanged with any Standard Webhooks library. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h, 6 h and 24 h.","operationId":"WebhooksController_trawl_workspace_createEndpoint","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"Idempotency-Key","in":"header","description":"Opaque client-generated string (1-256 chars, `[A-Za-z0-9_-:]`) that makes a retry of this request safe for 24 hours. The same key with a different body returns 409 `IDEMPOTENCY_KEY_REUSE`.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookEndpointDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecretDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Validation failure: URL not https with a public host, or an unknown event type (`WEBHOOK_EVENT_TYPE_UNKNOWN`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR","WEBHOOK_EVENT_TYPE_UNKNOWN"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.webhook.read` (reads) or `trawl.webhook.manage` (changes, Send test, replay), or Trawl is not enabled for the workspace (`SERVICE_NOT_ENABLED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Already 10 endpoints (`WEBHOOK_ENDPOINT_LIMIT`), or Idempotency-Key reused with a different body (`IDEMPOTENCY_KEY_REUSE`). Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","WEBHOOK_ENDPOINT_LIMIT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"The URL points at a private, loopback or internal address.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a webhook endpoint. Returns its signing secret once.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"create","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/endpoints/{endpoint_id}":{"get":{"operationId":"WebhooksController_trawl_workspace_getEndpoint","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"endpoint_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the endpoint."}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.webhook.read` (reads) or `trawl.webhook.manage` (changes, Send test, replay), or Trawl is not enabled for the workspace (`SERVICE_NOT_ENABLED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get one webhook endpoint (without its secret).","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"retrieve","bind":["workspace_id"],"paginated":false}},"patch":{"description":"Only the fields sent change. `enabled: true` (or a new URL) also clears an automatic disable. Switching off cancels queued retries. The secret is unchanged; rotate it with `POST …/rotate-secret`.","operationId":"WebhooksController_trawl_workspace_updateEndpoint","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"endpoint_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the endpoint."},{"name":"Idempotency-Key","in":"header","description":"Opaque client-generated string (1-256 chars, `[A-Za-z0-9_-:]`) that makes a retry of this request safe for 24 hours. The same key with a different body returns 409 `IDEMPOTENCY_KEY_REUSE`.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookEndpointDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Validation failure, or `endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.webhook.read` (reads) or `trawl.webhook.manage` (changes, Send test, replay), or Trawl is not enabled for the workspace (`SERVICE_NOT_ENABLED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Idempotency-Key reused with a different body (`IDEMPOTENCY_KEY_REUSE`). Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"The new URL points at a private, loopback or internal address.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Change a webhook endpoint: URL, description, events, on/off.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"update","bind":["workspace_id"],"paginated":false}},"delete":{"description":"Queued retries are dropped. Its past attempts stay in the event log until the events expire.","operationId":"WebhooksController_trawl_workspace_deleteEndpoint","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"endpoint_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the endpoint."}],"responses":{"204":{"description":"Deleted.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.webhook.read` (reads) or `trawl.webhook.manage` (changes, Send test, replay), or Trawl is not enabled for the workspace (`SERVICE_NOT_ENABLED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delete a webhook endpoint.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"delete","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/endpoints/{endpoint_id}/rotate-secret":{"post":{"description":"For `grace_hours` (default 24) the previous secret keeps signing too: `webhook-signature` then lists one `v1,` signature per secret, so receivers can switch over without dropping a delivery.","operationId":"WebhooksController_trawl_workspace_rotateSecret","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"endpoint_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the endpoint."},{"name":"Idempotency-Key","in":"header","description":"Opaque client-generated string (1-256 chars, `[A-Za-z0-9_-:]`) that makes a retry of this request safe for 24 hours. The same key with a different body returns 409 `IDEMPOTENCY_KEY_REUSE`.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateWebhookSecretDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecretDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`grace_hours` out of range, or `endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.webhook.read` (reads) or `trawl.webhook.manage` (changes, Send test, replay), or Trawl is not enabled for the workspace (`SERVICE_NOT_ENABLED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Idempotency-Key reused with a different body (`IDEMPOTENCY_KEY_REUSE`). Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Rotate the signing secret. The new one is returned once.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"rotateSecret","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/endpoints/{endpoint_id}/test":{"post":{"description":"Delivers the catalogue example of `event_type`, signed like any delivery, with `\"test\": true` in the body. Works while the endpoint is switched off or not subscribed to that type, is logged in the event log, and is never retried or counted toward an automatic disable. Waits up to 15 s for your receiver.","operationId":"WebhooksController_trawl_workspace_sendTest","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"endpoint_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the endpoint."},{"name":"Idempotency-Key","in":"header","description":"Opaque client-generated string (1-256 chars, `[A-Za-z0-9_-:]`) that makes a retry of this request safe for 24 hours. The same key with a different body returns 409 `IDEMPOTENCY_KEY_REUSE`.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendWebhookTestDto"}}}},"responses":{"200":{"description":"The attempt, successful or not.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryAttemptDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Unknown `event_type`, or `endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.webhook.read` (reads) or `trawl.webhook.manage` (changes, Send test, replay), or Trawl is not enabled for the workspace (`SERVICE_NOT_ENABLED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Idempotency-Key reused with a different body (`IDEMPOTENCY_KEY_REUSE`). Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Send a test event to this endpoint now and return the result.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"test","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/events":{"get":{"description":"Every event logged while at least one endpoint existed, with the latest state per endpoint and when the next retry is due.","operationId":"WebhooksController_trawl_workspace_listEvents","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"type","required":false,"in":"query","description":"Only these types. Repeat the parameter or comma-separate.","schema":{"type":"array","items":{"type":"string","enum":["job.succeeded","job.failed","credits.low"]}}},{"name":"endpoint_id","required":false,"in":"query","description":"Only events sent (or queued) to this endpoint.","schema":{"format":"uuid","type":"string"}},{"name":"status","required":false,"in":"query","description":"Only events in this delivery status.","schema":{"$ref":"#/components/schemas/WebhookEventStatus"}},{"name":"created_after","required":false,"in":"query","description":"Only events created at or after this instant.","schema":{"format":"date-time","type":"string"}},{"name":"created_before","required":false,"in":"query","description":"Only events created before this instant.","schema":{"format":"date-time","type":"string"}},{"name":"limit","required":false,"in":"query","description":"Page size.","schema":{"minimum":1,"maximum":100,"default":20,"type":"integer"}},{"name":"cursor","required":false,"in":"query","description":"The previous page's `pagination.next_cursor`.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWebhookEventsResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Invalid filter value or cursor.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.webhook.read` (reads) or `trawl.webhook.manage` (changes, Send test, replay), or Trawl is not enabled for the workspace (`SERVICE_NOT_ENABLED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List webhook events, newest first (kept for 30 days).","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.events","method":"list","bind":["workspace_id"],"paginated":true}}},"/v1/workspaces/{workspace_id}/webhooks/events/{event_id}":{"get":{"operationId":"WebhooksController_trawl_workspace_getEvent","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"event_id","required":true,"in":"path","description":"The event id, `evt_<uuid>`.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEventDetailDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`event_id` is not `evt_<uuid>`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.webhook.read` (reads) or `trawl.webhook.manage` (changes, Send test, replay), or Trawl is not enabled for the workspace (`SERVICE_NOT_ENABLED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such event here (`WEBHOOK_EVENT_NOT_FOUND`); events are kept for 30 days.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_EVENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get one webhook event with every delivery attempt.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.events","method":"retrieve","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/events/{event_id}/replay":{"post":{"description":"Same body and `webhook-id`, freshly signed, to every switched-on endpoint that receives this event type now, or only to `endpoint_id`. Waits for the receivers (up to 15 s each) and returns the attempts. A success cancels a retry still queued for that endpoint.","operationId":"WebhooksController_trawl_workspace_replay","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"event_id","required":true,"in":"path","description":"The event id, `evt_<uuid>`.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"}},{"name":"endpoint_id","required":false,"in":"query","description":"Send to this endpoint only.","schema":{"format":"uuid","type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Opaque client-generated string (1-256 chars, `[A-Za-z0-9_-:]`) that makes a retry of this request safe for 24 hours. The same key with a different body returns 409 `IDEMPOTENCY_KEY_REUSE`.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplayWebhookEventResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`event_id` is not `evt_<uuid>`, or `endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.webhook.read` (reads) or `trawl.webhook.manage` (changes, Send test, replay), or Trawl is not enabled for the workspace (`SERVICE_NOT_ENABLED`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such event here (`WEBHOOK_EVENT_NOT_FOUND`); events are kept for 30 days. Or No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_EVENT_NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`endpoint_id` is switched off or does not receive this event type (`WEBHOOK_ENDPOINT_NOT_SUBSCRIBED`), or Idempotency-Key reused with a different body (`IDEMPOTENCY_KEY_REUSE`). Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","WEBHOOK_ENDPOINT_NOT_SUBSCRIBED","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Send a logged event again, to its endpoints or to one endpoint.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.events","method":"replay","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks":{"post":{"deprecated":true,"description":"Deprecated: use `/v1/workspaces/{workspace_id}/webhooks/endpoints` (event filter, event descriptions, Send test that reports the result, event log, replay, rotation with a grace period). Responses carry `Deprecation: true` and `Sunset: Fri, 15 Jan 2027 00:00:00 GMT`.","operationId":"WebhookController_create","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"Idempotency-Key","in":"header","description":"Stripe-style idempotency key. The signing secret is returned exactly once; a retry without this header creates a second webhook + secret. 24h TTL.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookDto"}}}},"responses":{"201":{"description":"Webhook created. Includes plaintext `signing_secret` exactly once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreateResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Invalid URL (must be public HTTPS).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"The workspace already has 10 webhooks. Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create a workspace webhook. The response includes the signing secret in clear EXACTLY ONCE — copy it now.","tags":["webhooks"],"x-pc-sdk":false},"get":{"deprecated":true,"description":"Deprecated: use `/v1/workspaces/{workspace_id}/webhooks/endpoints` (event filter, event descriptions, Send test that reports the result, event log, replay, rotation with a grace period). Responses carry `Deprecation: true` and `Sunset: Fri, 15 Jan 2027 00:00:00 GMT`.","operationId":"WebhookController_list","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from the previous response's `pagination.next_cursor`.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Page size. Defaults to 20, capped at 100."}],"responses":{"200":{"description":"Page of webhooks with a pagination cursor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookListResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Invalid cursor or limit.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List webhooks for the workspace (paginated, newest first, secrets masked)","tags":["webhooks"],"x-pc-sdk":false}},"/v1/workspaces/{workspace_id}/webhooks/{id}":{"get":{"deprecated":true,"description":"Deprecated: use `/v1/workspaces/{workspace_id}/webhooks/endpoints` (event filter, event descriptions, Send test that reports the result, event log, replay, rotation with a grace period). Responses carry `Deprecation: true` and `Sunset: Fri, 15 Jan 2027 00:00:00 GMT`.","operationId":"WebhookController_get","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the webhook."}],"responses":{"200":{"description":"Webhook.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Webhook not found.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a webhook (secret masked)","tags":["webhooks"],"x-pc-sdk":false},"patch":{"deprecated":true,"description":"Deprecated: use `/v1/workspaces/{workspace_id}/webhooks/endpoints` (event filter, event descriptions, Send test that reports the result, event log, replay, rotation with a grace period). Responses carry `Deprecation: true` and `Sunset: Fri, 15 Jan 2027 00:00:00 GMT`.","operationId":"WebhookController_update","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the webhook."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookDto"}}}},"responses":{"200":{"description":"Updated webhook (secret masked).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Invalid URL.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Webhook not found.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update url / active flag (active=true clears auto-disable)","tags":["webhooks"],"x-pc-sdk":false},"delete":{"deprecated":true,"description":"Deprecated: use `/v1/workspaces/{workspace_id}/webhooks/endpoints` (event filter, event descriptions, Send test that reports the result, event log, replay, rotation with a grace period). Responses carry `Deprecation: true` and `Sunset: Fri, 15 Jan 2027 00:00:00 GMT`.","operationId":"WebhookController_remove","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the webhook."}],"responses":{"204":{"description":"Deleted.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"404":{"description":"Webhook not found.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delete a webhook","tags":["webhooks"],"x-pc-sdk":false}},"/v1/workspaces/{workspace_id}/webhooks/{id}/deliveries":{"get":{"deprecated":true,"description":"Deprecated: use `/v1/workspaces/{workspace_id}/webhooks/endpoints` (event filter, event descriptions, Send test that reports the result, event log, replay, rotation with a grace period). Responses carry `Deprecation: true` and `Sunset: Fri, 15 Jan 2027 00:00:00 GMT`.","operationId":"WebhookController_recentDeliveries","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the webhook."},{"name":"status","required":false,"in":"query","description":"Filter by outcome. `succeeded` = 2xx response; `failed` = any non-2xx, including attempts where the destination never responded.","schema":{"type":"string","enum":["succeeded","failed"]}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from the previous response's `pagination.next_cursor`.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Page size. Defaults to 50, capped at 200."}],"responses":{"200":{"description":"Page of delivery attempts with a pagination cursor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryListResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Invalid cursor, limit or `status` filter.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"Webhook not found in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delivery attempts for a webhook (paginated, default 50, max 200, newest first)","tags":["webhooks"],"x-pc-sdk":false}},"/v1/workspaces/{workspace_id}/webhooks/{id}/rotate-secret":{"post":{"deprecated":true,"description":"Deprecated: use `/v1/workspaces/{workspace_id}/webhooks/endpoints` (event filter, event descriptions, Send test that reports the result, event log, replay, rotation with a grace period). Responses carry `Deprecation: true` and `Sunset: Fri, 15 Jan 2027 00:00:00 GMT`.","operationId":"WebhookController_rotateSecret","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the webhook."}],"responses":{"201":{"description":"Secret rotated. Includes the new plaintext secret exactly once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookRotateSecretResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"404":{"description":"Webhook not found.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Rotate the signing secret. Returns the new secret in clear EXACTLY ONCE; the old one stops at once.","tags":["webhooks"],"x-pc-sdk":false}},"/v1/workspaces/{workspace_id}/webhooks/{id}/test":{"post":{"deprecated":true,"description":"Deprecated: use `/v1/workspaces/{workspace_id}/webhooks/endpoints` (event filter, event descriptions, Send test that reports the result, event log, replay, rotation with a grace period). Responses carry `Deprecation: true` and `Sunset: Fri, 15 Jan 2027 00:00:00 GMT`.","operationId":"WebhookController_fireTest","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the webhook."}],"responses":{"202":{"description":"Sent. Read the outcome from `/deliveries`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestAcceptedDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"404":{"description":"Webhook not found.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"The webhook is switched off.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Send the `job.succeeded` example to this webhook; the attempt shows in `/deliveries`","tags":["webhooks"],"x-pc-sdk":false}},"/v1/workspaces/{workspace_id}/jobs":{"post":{"operationId":"JobController_create","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"Idempotency-Key","in":"header","description":"Pass a key (1..256 chars, [A-Za-z0-9_\\-:]) to make retries safe — replays return the original response for 24h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJobDto"}}}},"responses":{"201":{"description":"Job created and queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Invalid JSON Schema (must be a non-empty `type: object`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.create`, or workspace has not enabled the `trawl` service.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"Rate limited. `RATE_LIMITED` — submission window exceeded (default 10/min per workspace); the `Retry-After` header gives the seconds until the window resets. `TOO_MANY_ACTIVE_JOBS` — too many queued/running jobs (default 25); retry once some finish. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED","TOO_MANY_ACTIVE_JOBS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create (enqueue) a trawl job","tags":["jobs"],"x-pc-sdk":{"resource":"jobs","method":"create","bind":["workspace_id"],"paginated":false}},"get":{"operationId":"JobController_list","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"status","required":false,"in":"query","description":"Filter by job status.","schema":{"$ref":"#/components/schemas/JobStatus"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor for pagination.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Page size.","schema":{"minimum":1,"maximum":100,"default":20,"type":"integer"}}],"responses":{"200":{"description":"Page of jobs with pagination cursor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobListResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"403":{"description":"Caller lacks `trawl.list`, or workspace has not enabled the `trawl` service.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List trawl jobs (paginated, newest first)","tags":["jobs"],"x-pc-sdk":{"resource":"jobs","method":"list","bind":["workspace_id"],"paginated":true}}},"/v1/workspaces/{workspace_id}/jobs/{id}":{"get":{"operationId":"JobController_get","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the job."}],"responses":{"200":{"description":"Job row.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"404":{"description":"Job not found in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a single trawl job","tags":["jobs"],"x-pc-sdk":{"resource":"jobs","method":"retrieve","bind":["workspace_id"],"paginated":false,"poll":{"field":"status","terminal":["succeeded","failed","cancelled"]}}}},"/v1/workspaces/{workspace_id}/jobs/{id}/cancel":{"post":{"operationId":"JobController_cancel","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the job."}],"responses":{"200":{"description":"Job cancelled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"404":{"description":"Job not found in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"Job is already in a terminal state and cannot be cancelled.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Cancel a queued or running trawl job","tags":["jobs"],"x-pc-sdk":{"resource":"jobs","method":"cancel","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/credits":{"get":{"operationId":"CreditsController_get","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace."},{"name":"limit","required":false,"in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"How many recent entries to return. Defaults to 50, capped at 200."}],"responses":{"200":{"description":"Balance, low-balance state and recent entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditsResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing or invalid bearer token.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `trawl.read`, or the service is not enabled.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Remaining Trawl steps for this workspace, whether the balance is low, and recent ledger entries","tags":["credits"],"x-pc-sdk":{"resource":"credits","method":"retrieve","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/stats":{"get":{"description":"Total jobs, jobs created / succeeded / failed and steps used in the window, the credit balance, when the latest job was created and a daily jobs series.\n\n**Active** means a job was created.\n\nDays are UTC. Counts only: no target URLs, descriptions or job results. Cached for up to 60 seconds (`generated_at`).","operationId":"WorkspaceStatsController_get","parameters":[{"name":"workspace_id","required":true,"in":"path","description":"Workspace id.","schema":{"format":"uuid","type":"string"}},{"name":"window","required":false,"in":"query","description":"Window for `in_window` and the length of `series`, in UTC days ending today.","schema":{"$ref":"#/components/schemas/StatsWindow"}}],"responses":{"200":{"description":"Trawl stats.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrawlWorkspaceStatsDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`window` is not one of 1d, 7d, 30d.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"]},"403":{"description":"Caller lacks `trawl.read` on the workspace, or Trawl is not enabled on it (`SERVICE_NOT_ENABLED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"]},"404":{"description":"Workspace not found: the id is unknown, is not a UUID, or is not visible to the caller. Same answer as the service's other workspace routes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND"]},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Trawl stats for a workspace","tags":["stats"],"x-pc-sdk":{"resource":"stats","method":"retrieve","bind":["workspace_id"],"paginated":false}}}},"info":{"title":"Trawl API","description":"AI web extraction for the ProductCraft platform: submit a URL and a JSON Schema, get back structured JSON. Jobs run asynchronously; poll the job or subscribe a webhook endpoint for its result.\n\n## Rate limits\n\nEvery `/v1` route is limited per client IP to 20 requests per second and 600 per minute. Each response carries `X-RateLimit-Limit-short`, `X-RateLimit-Remaining-short`, `X-RateLimit-Reset-short` (the one-second window) and the same three with `-long` (the one-minute window). Over either limit the response is `429` with code `RATE_LIMITED` and a `Retry-After` header in seconds.","version":"0.1.0","contact":{}},"tags":[{"name":"credits","description":"The workspace's Trawl credit balance and recent ledger entries."},{"name":"jobs","description":"Extraction jobs: submit a URL and a JSON Schema, then read the structured result when the job completes."},{"name":"stats","description":"Usage totals and a time series for the workspace dashboard."},{"name":"webhooks","description":"Webhook endpoints that receive `job.succeeded`, `job.failed` and `credits.low` events, their delivery log and replays. The legacy single-webhook routes are deprecated."}],"servers":[{"url":"https://api.trawl.productcraft.co","description":"Trawl API"}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"WebhookEventType":{"type":"string","enum":["job.succeeded","job.failed","credits.low"]},"WebhookEventTypeDto":{"type":"object","properties":{"type":{"example":"job.succeeded","description":"The event type (one of this product's catalogue).","allOf":[{"$ref":"#/components/schemas/WebhookEventType"}]},"description":{"type":"string","example":"A user signed up or was created by an admin.","description":"When this event is sent."},"category":{"type":"string","example":"Users","description":"Group shown in the event picker."},"example":{"type":"object","additionalProperties":true,"description":"An example `data` object; \"Send test\" delivers exactly this."}},"x-pc-sdk-model":"WebhookEventTypeInfo","required":["type","description","category","example"]},"ListWebhookEventTypesResponseDto":{"type":"object","properties":{"data":{"description":"Every event type this product sends.","type":"array","items":{"$ref":"#/components/schemas/WebhookEventTypeDto"}}},"required":["data"],"x-pc-sdk-model":"ListWebhookEventTypes"},"WebhookEndpointStatus":{"type":"string","enum":["enabled","disabled","auto_disabled"]},"WebhookEndpointDisabledReason":{"type":"string","enum":["manual","failing"]},"WebhookEndpointDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The endpoint's id."},"scope_type":{"type":"string","example":"workspace","description":"What owns the endpoint: `workspace`, `app` or `community`."},"scope_id":{"type":"string","description":"Id of the owning workspace, app or community."},"url":{"type":"string","format":"uri","example":"https://api.acme.com/webhooks","description":"Where events are sent."},"description":{"type":"string","nullable":true,"example":"Production receiver","description":"A label for people; null when unset."},"event_types":{"description":"Subscribed event types. Empty means every event, including types added later.","example":[],"type":"array","items":{"type":"string"}},"enabled":{"type":"boolean","description":"The on/off switch (`PATCH … { enabled }`)."},"status":{"description":"`enabled` receives events. `disabled` was switched off. `auto_disabled` was switched off after 20 failed deliveries in a row or 24 hours of failures; `PATCH { enabled: true }` turns it back on.","allOf":[{"$ref":"#/components/schemas/WebhookEndpointStatus"}]},"disabled_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was switched off; null while enabled."},"disabled_reason":{"nullable":true,"description":"`manual`: switched off by a caller. `failing`: switched off automatically after failed deliveries. Null while enabled.","allOf":[{"$ref":"#/components/schemas/WebhookEndpointDisabledReason"}]},"consecutive_failures":{"type":"number","description":"Failed automatic deliveries in a row; a success resets it."},"first_failure_at":{"type":"string","format":"date-time","nullable":true,"description":"Start of the current failure streak."},"signing_secret_hint":{"type":"string","description":"Last 4 characters of the signing secret.","example":"x9Q="},"prev_signing_secret_active":{"type":"boolean","description":"True while the previous secret still signs too (after a rotation, for its grace period)."},"prev_signing_secret_expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Until then the previous secret signs too (after a rotation)."},"legacy_signature":{"type":"boolean","description":"True for endpoints carried over from the product's previous webhook system: they also receive the old signature header until its sunset date."},"created_at":{"type":"string","format":"date-time","description":"When the endpoint was created."},"updated_at":{"type":"string","format":"date-time","description":"When the endpoint last changed."}},"required":["id","scope_type","scope_id","url","description","event_types","enabled","status","disabled_at","disabled_reason","consecutive_failures","first_failure_at","signing_secret_hint","prev_signing_secret_active","prev_signing_secret_expires_at","legacy_signature","created_at","updated_at"],"x-pc-sdk-model":"WebhookEndpoint"},"WebhookPaginationDto":{"type":"object","properties":{"next_cursor":{"type":"string","nullable":true,"description":"Pass as `cursor` to get the next page; null on the last page."},"has_more":{"type":"boolean","description":"Whether another page follows."}},"required":["next_cursor","has_more"],"x-pc-sdk-model":"WebhookPagination"},"ListWebhookEndpointsResponseDto":{"type":"object","properties":{"data":{"description":"The endpoints, newest first.","type":"array","items":{"$ref":"#/components/schemas/WebhookEndpointDto"}},"pagination":{"description":"Always one page (an endpoint limit applies).","allOf":[{"$ref":"#/components/schemas/WebhookPaginationDto"}]}},"required":["data","pagination"],"x-pc-sdk-model":"ListWebhookEndpoints"},"CreateWebhookEndpointDto":{"type":"object","properties":{"url":{"type":"string","description":"Where to send events: an absolute https:// URL on a public host.","example":"https://api.acme.com/webhooks","maxLength":2048,"format":"uri"},"description":{"type":"string","nullable":true,"maxLength":200,"example":"Production receiver","description":"A label for people."},"event_types":{"type":"array","description":"Event types from `GET …/webhooks/event-types`. Empty or omitted means every event, including types added later.","example":["job.succeeded","job.failed"],"items":{"type":"string","enum":["job.succeeded","job.failed","credits.low"]}},"enabled":{"type":"boolean","default":true,"description":"Create it switched off."}},"required":["url"]},"WebhookEndpointWithSecretDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The endpoint's id."},"scope_type":{"type":"string","example":"workspace","description":"What owns the endpoint: `workspace`, `app` or `community`."},"scope_id":{"type":"string","description":"Id of the owning workspace, app or community."},"url":{"type":"string","format":"uri","example":"https://api.acme.com/webhooks","description":"Where events are sent."},"description":{"type":"string","nullable":true,"example":"Production receiver","description":"A label for people; null when unset."},"event_types":{"description":"Subscribed event types. Empty means every event, including types added later.","example":[],"type":"array","items":{"type":"string"}},"enabled":{"type":"boolean","description":"The on/off switch (`PATCH … { enabled }`)."},"status":{"description":"`enabled` receives events. `disabled` was switched off. `auto_disabled` was switched off after 20 failed deliveries in a row or 24 hours of failures; `PATCH { enabled: true }` turns it back on.","allOf":[{"$ref":"#/components/schemas/WebhookEndpointStatus"}]},"disabled_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was switched off; null while enabled."},"disabled_reason":{"nullable":true,"description":"`manual`: switched off by a caller. `failing`: switched off automatically after failed deliveries. Null while enabled.","allOf":[{"$ref":"#/components/schemas/WebhookEndpointDisabledReason"}]},"consecutive_failures":{"type":"number","description":"Failed automatic deliveries in a row; a success resets it."},"first_failure_at":{"type":"string","format":"date-time","nullable":true,"description":"Start of the current failure streak."},"signing_secret_hint":{"type":"string","description":"Last 4 characters of the signing secret.","example":"x9Q="},"prev_signing_secret_active":{"type":"boolean","description":"True while the previous secret still signs too (after a rotation, for its grace period)."},"prev_signing_secret_expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Until then the previous secret signs too (after a rotation)."},"legacy_signature":{"type":"boolean","description":"True for endpoints carried over from the product's previous webhook system: they also receive the old signature header until its sunset date."},"created_at":{"type":"string","format":"date-time","description":"When the endpoint was created."},"updated_at":{"type":"string","format":"date-time","description":"When the endpoint last changed."},"signing_secret":{"type":"string","description":"The signing secret, shown once: pass it unchanged to any Standard Webhooks verifier. Store it now; it cannot be shown again.","example":"whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"}},"required":["id","scope_type","scope_id","url","description","event_types","enabled","status","disabled_at","disabled_reason","consecutive_failures","first_failure_at","signing_secret_hint","prev_signing_secret_active","prev_signing_secret_expires_at","legacy_signature","created_at","updated_at","signing_secret"],"x-pc-sdk-model":"WebhookEndpointWithSecret"},"UpdateWebhookEndpointDto":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"format":"uri","example":"https://api.acme.com/webhooks","description":"A new absolute https:// URL on a public host."},"description":{"type":"string","nullable":true,"maxLength":200,"description":"A new label; null clears it."},"event_types":{"type":"array","description":"Replaces the subscription. Event types from `GET …/webhooks/event-types`. Empty or omitted means every event, including types added later.","items":{"type":"string","enum":["job.succeeded","job.failed","credits.low"]}},"enabled":{"type":"boolean","description":"Switch it on or off. Switching on also clears an automatic disable."}}},"RotateWebhookSecretDto":{"type":"object","properties":{"grace_hours":{"type":"number","default":24,"minimum":0,"maximum":168,"description":"Hours the previous secret keeps signing too (0 ends it now)."}}},"SendWebhookTestDto":{"type":"object","properties":{"event_type":{"type":"string","enum":["job.succeeded","job.failed","credits.low"],"description":"Which event to send; its catalogue example is the `data`.","example":"job.succeeded"}},"required":["event_type"]},"WebhookAttemptTrigger":{"type":"string","enum":["automatic","replay","test"]},"WebhookDeliveryAttemptDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The attempt's id."},"event_id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event that was delivered."},"endpoint_id":{"type":"string","format":"uuid","description":"The endpoint it was delivered to."},"event_type":{"type":"string","example":"user.created","description":"The event's type."},"attempt_number":{"type":"number","description":"1 for the first delivery; retries and replays count up."},"trigger":{"description":"`automatic` (first delivery or a retry), `replay` or `test`.","allOf":[{"$ref":"#/components/schemas/WebhookAttemptTrigger"}]},"url":{"type":"string","format":"uri","description":"Where it was sent."},"status_code":{"type":"number","nullable":true,"description":"HTTP status; null when no response came back."},"succeeded":{"type":"boolean","description":"True for a 2xx response."},"error_message":{"type":"string","nullable":true,"example":"request timed out","description":"Why the attempt failed when no 2xx came back; null on success."},"response_body":{"type":"string","nullable":true,"description":"The first 1 KiB of the response body."},"latency_ms":{"type":"number","nullable":true,"description":"Milliseconds until the response; null when none came back."},"attempted_at":{"type":"string","format":"date-time","description":"When the attempt was made."}},"required":["id","event_id","endpoint_id","event_type","attempt_number","trigger","url","status_code","succeeded","error_message","response_body","latency_ms","attempted_at"],"x-pc-sdk-model":"WebhookDeliveryAttempt"},"WebhookEventStatus":{"type":"string","enum":["pending","delivered","failed","not_sent"]},"WebhookEventDeliveryDto":{"type":"object","properties":{"endpoint_id":{"type":"string","format":"uuid","description":"The endpoint."},"attempt_number":{"type":"number","description":"Number of the latest attempt; retries and replays count up."},"status_code":{"type":"number","nullable":true,"description":"HTTP status of the latest attempt; null when no response came back."},"succeeded":{"type":"boolean","description":"True when the latest attempt got a 2xx."},"attempted_at":{"type":"string","format":"date-time","description":"When the latest attempt was made."},"next_attempt_at":{"type":"string","format":"date-time","nullable":true,"description":"When the next automatic attempt is due; null when none is queued."}},"required":["endpoint_id","attempt_number","status_code","succeeded","attempted_at","next_attempt_at"],"x-pc-sdk-model":"WebhookEventDelivery"},"WebhookEventDto":{"type":"object","properties":{"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"Sent as `webhook-id`; the same on every retry and replay."},"type":{"example":"job.succeeded","description":"The event type (one of this product's catalogue).","allOf":[{"$ref":"#/components/schemas/WebhookEventType"}]},"created_at":{"type":"string","format":"date-time","description":"When the event happened."},"test":{"type":"boolean","description":"Made by \"Send test\"."},"status":{"description":"`pending`: a delivery is queued. `failed`: the latest attempt to some endpoint failed and none is queued. `delivered`: every endpoint answered 2xx. `not_sent`: no endpoint subscribed.","allOf":[{"$ref":"#/components/schemas/WebhookEventStatus"}]},"payload":{"type":"object","additionalProperties":true,"description":"The JSON body exactly as sent."},"deliveries":{"description":"Latest attempt per endpoint (an endpoint whose first attempt is still queued is not listed yet).","type":"array","items":{"$ref":"#/components/schemas/WebhookEventDeliveryDto"}}},"required":["id","type","created_at","test","status","payload","deliveries"],"x-pc-sdk-model":"WebhookEvent"},"ListWebhookEventsResponseDto":{"type":"object","properties":{"data":{"description":"The events, newest first.","type":"array","items":{"$ref":"#/components/schemas/WebhookEventDto"}},"pagination":{"description":"The cursor for the next page.","allOf":[{"$ref":"#/components/schemas/WebhookPaginationDto"}]}},"required":["data","pagination"],"x-pc-sdk-model":"ListWebhookEvents"},"WebhookEventDetailDto":{"type":"object","properties":{"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"Sent as `webhook-id`; the same on every retry and replay."},"type":{"example":"job.succeeded","description":"The event type (one of this product's catalogue).","allOf":[{"$ref":"#/components/schemas/WebhookEventType"}]},"created_at":{"type":"string","format":"date-time","description":"When the event happened."},"test":{"type":"boolean","description":"Made by \"Send test\"."},"status":{"description":"`pending`: a delivery is queued. `failed`: the latest attempt to some endpoint failed and none is queued. `delivered`: every endpoint answered 2xx. `not_sent`: no endpoint subscribed.","allOf":[{"$ref":"#/components/schemas/WebhookEventStatus"}]},"payload":{"type":"object","additionalProperties":true,"description":"The JSON body exactly as sent."},"deliveries":{"description":"Latest attempt per endpoint (an endpoint whose first attempt is still queued is not listed yet).","type":"array","items":{"$ref":"#/components/schemas/WebhookEventDeliveryDto"}},"attempts":{"description":"Every attempt, newest first.","type":"array","items":{"$ref":"#/components/schemas/WebhookDeliveryAttemptDto"}}},"required":["id","type","created_at","test","status","payload","deliveries","attempts"],"x-pc-sdk-model":"WebhookEventDetail"},"ReplayWebhookEventResponseDto":{"type":"object","properties":{"event_id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","description":"The replayed event (the same id as the original)."},"attempts":{"description":"One attempt per endpoint it was sent to.","type":"array","items":{"$ref":"#/components/schemas/WebhookDeliveryAttemptDto"}}},"required":["event_id","attempts"],"x-pc-sdk-model":"ReplayWebhookEvent"},"CreateWebhookDto":{"type":"object","properties":{"url":{"type":"string","description":"Destination URL (https required in prod)."},"active":{"type":"boolean","description":"Disabled webhooks are never delivered to."}},"required":["url"]},"WebhookCreateResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workspace_id":{"type":"string","format":"uuid"},"url":{"type":"string"},"active":{"type":"boolean","description":"Whether it receives events (`enabled` on the new routes)."},"failure_count":{"type":"number","description":"Failed deliveries in a row (`consecutive_failures` on the new routes)."},"last_status_code":{"type":"number","nullable":true,"description":"HTTP status of the latest attempt."},"last_attempt_at":{"type":"string","nullable":true,"format":"date-time","description":"When the latest attempt was made."},"auto_disabled_at":{"type":"string","nullable":true,"format":"date-time","description":"Set when it was switched off automatically after sustained failures."},"first_failure_at":{"type":"string","nullable":true,"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"secret_set":{"type":"boolean","description":"Always true: every webhook has a signing secret."},"signing_secret":{"type":"string","description":"Plaintext signing secret (`whsec_…`), returned EXACTLY ONCE. Persist immediately; it does not appear on later reads."}},"required":["id","workspace_id","url","active","failure_count","last_status_code","last_attempt_at","auto_disabled_at","first_failure_at","created_at","signing_secret"]},"WebhookResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workspace_id":{"type":"string","format":"uuid"},"url":{"type":"string"},"active":{"type":"boolean","description":"Whether it receives events (`enabled` on the new routes)."},"failure_count":{"type":"number","description":"Failed deliveries in a row (`consecutive_failures` on the new routes)."},"last_status_code":{"type":"number","nullable":true,"description":"HTTP status of the latest attempt."},"last_attempt_at":{"type":"string","nullable":true,"format":"date-time","description":"When the latest attempt was made."},"auto_disabled_at":{"type":"string","nullable":true,"format":"date-time","description":"Set when it was switched off automatically after sustained failures."},"first_failure_at":{"type":"string","nullable":true,"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"secret_set":{"type":"boolean","description":"Always true: every webhook has a signing secret."}},"required":["id","workspace_id","url","active","failure_count","last_status_code","last_attempt_at","auto_disabled_at","first_failure_at","created_at"]},"WebhookListPaginationDto":{"type":"object","properties":{"next_cursor":{"type":"string","nullable":true},"has_more":{"type":"boolean"}},"required":["next_cursor","has_more"]},"WebhookListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookResponseDto"}},"pagination":{"$ref":"#/components/schemas/WebhookListPaginationDto"}},"required":["data","pagination"]},"WebhookDeliveryResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"webhook_id":{"type":"string","format":"uuid"},"job_id":{"type":"string","nullable":true,"description":"The job the event was about; null for an event that is not about a job (`credits.low`)."},"event_id":{"type":"string","description":"Id of the event (`evt_…`), the same across retries."},"event_type":{"type":"string","example":"job.succeeded"},"attempt_number":{"type":"number","description":"Which attempt this was for the event: 1 for the first delivery, then 2 to 7 for the automatic retries (1m, 5m, 30m, 2h, 6h and 24h after each failure). Retries keep the same `event_id`. A test or a replay is numbered in the same sequence.","minimum":1},"status_code":{"type":"number","nullable":true,"description":"HTTP status returned by the destination; null when no response was received."},"response_body_truncated":{"type":"string","nullable":true},"error_message":{"type":"string","nullable":true},"latency_ms":{"type":"number","nullable":true},"attempted_at":{"type":"string","format":"date-time"}},"required":["id","webhook_id","job_id","event_id","event_type","attempt_number","status_code","response_body_truncated","error_message","latency_ms","attempted_at"]},"WebhookDeliveryListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDeliveryResponseDto"}},"pagination":{"$ref":"#/components/schemas/WebhookListPaginationDto"}},"required":["data","pagination"]},"UpdateWebhookDto":{"type":"object","properties":{"url":{"type":"string","description":"New destination URL."},"active":{"type":"boolean","description":"Pause/resume the webhook. Setting active=true clears auto-disable."}}},"WebhookRotateSecretResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"secret":{"type":"string","description":"New plaintext signing secret, returned EXACTLY ONCE. Persist immediately; the old secret stops working at once."}},"required":["id","secret"]},"WebhookTestAcceptedDto":{"type":"object","properties":{"accepted":{"type":"boolean","example":true}},"required":["accepted"]},"CreateJobDto":{"type":"object","properties":{"json_schema":{"type":"object","description":"JSON Schema describing the shape of the extraction result. Must be a non-empty object with `type: \"object\"`.","additionalProperties":true,"example":{"type":"object","properties":{"title":{"type":"string"}},"required":["title"]}},"description":{"type":"string","description":"Natural-language description of what to extract.","example":"Extract the product title and price from each page."},"suggested_urls":{"description":"Seed URLs the worker should start from.","maxItems":50,"type":"array","items":{"type":"string"}},"webhook_id":{"type":"string","format":"uuid","description":"A webhook endpoint of this workspace to notify when the job finishes. Endpoints made before 17 Oct 2026 receive only the jobs that name them here; endpoints made under `/webhooks/endpoints` receive every job event they subscribe to, so leave this out for them."},"max_steps":{"type":"number","description":"Maximum number of agent steps (one LLM call each) before the job stops. Defaults to 20. Multi-source extractions typically need 40+. The server additionally clamps this to an operator ceiling, so a very large value does not guarantee a very long run — `timeout_s` still applies.","minimum":1,"maximum":1000,"default":20},"timeout_s":{"type":"number","description":"Wall-clock timeout for the job, in seconds.","minimum":1,"maximum":3600},"model_tier":{"type":"string","description":"Model tier the worker should use (e.g. `standard`, `pro`)."}},"required":["json_schema","description"]},"JobStatus":{"type":"string","enum":["queued","running","succeeded","failed","cancelled"]},"JobResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"workspace_id":{"type":"string","format":"uuid","description":"The workspace that owns the job."},"status":{"description":"`queued` until a worker picks it up, then `running`; it ends `succeeded` (the result is in `result`), `failed` (see `error`) or `cancelled`.","allOf":[{"$ref":"#/components/schemas/JobStatus"}]},"json_schema":{"type":"object","description":"The JSON Schema the job was created with.","additionalProperties":true},"description":{"type":"string","description":"Natural-language description of what to extract."},"suggested_urls":{"nullable":true,"description":"Seed URLs the worker starts from; null when none were given.","type":"array","items":{"type":"string"}},"max_steps":{"type":"integer","description":"Maximum number of agent steps (one LLM call each) before the job stops."},"timeout_s":{"type":"integer","description":"Wall-clock timeout for the job, in seconds."},"model_tier":{"type":"string","description":"Model tier the worker uses (e.g. `standard`, `pro`)."},"webhook_id":{"type":"string","nullable":true,"format":"uuid","description":"The webhook endpoint named at creation to notify when the job ends; null when none."},"result":{"type":"object","description":"The extracted data, shaped by `json_schema`. Null until the job succeeds.","nullable":true,"additionalProperties":true},"error":{"type":"string","nullable":true,"description":"Why the job failed; null unless `status` is `failed`."},"attempts":{"type":"integer","description":"How many times a worker has picked the job up."},"enqueued_at":{"type":"string","format":"date-time","description":"When the job was queued."},"started_at":{"type":"string","nullable":true,"format":"date-time","description":"When a worker started it; null while queued."},"completed_at":{"type":"string","nullable":true,"format":"date-time","description":"When it ended; null until then."},"created_at":{"type":"string","format":"date-time","description":"When the job was created."}},"required":["id","workspace_id","status","json_schema","description","suggested_urls","max_steps","timeout_s","model_tier","webhook_id","result","error","attempts","enqueued_at","started_at","completed_at","created_at"],"x-pc-sdk-model":"Job"},"JobListPaginationDto":{"type":"object","properties":{"next_cursor":{"type":"string","nullable":true,"description":"Pass as `cursor` to get the next page; null on the last page."},"has_more":{"type":"boolean","description":"Whether another page follows."}},"required":["next_cursor","has_more"],"x-pc-sdk-model":"JobListPagination"},"JobListResponseDto":{"type":"object","properties":{"data":{"description":"The jobs, newest first.","type":"array","items":{"$ref":"#/components/schemas/JobResponseDto"}},"pagination":{"description":"The cursor for the next page.","allOf":[{"$ref":"#/components/schemas/JobListPaginationDto"}]}},"required":["data","pagination"],"x-pc-sdk-model":"JobList"},"CreditLedgerEntryDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The entry's id."},"delta_steps":{"type":"number","description":"Steps added (positive: allowance, top-up, grant) or used (negative: a job).","example":-12},"reason":{"type":"string","description":"Why: `initial_free_allowance`, `grant`, `job`, …","example":"job"},"job_id":{"type":"string","nullable":true,"format":"uuid","description":"The job, for a `job` entry."},"created_at":{"type":"string","format":"date-time","description":"When the entry was recorded."}},"required":["id","delta_steps","reason","job_id","created_at"],"x-pc-sdk-model":"CreditLedgerEntry"},"CreditsResponseDto":{"type":"object","properties":{"balance_steps":{"type":"number","description":"Remaining steps. One agent step uses one credit; new jobs are refused (402) at zero. Can dip slightly below zero, because a running job is always allowed to finish.","example":8420},"low_balance":{"type":"boolean","description":"True when the balance is under `low_balance_threshold_steps`. The `credits.low` webhook fires once when this turns true, and again only after the next top-up.","example":false},"low_balance_threshold_steps":{"type":"number","description":"The low-balance line: 10% of the last top-up, and never under 100 steps.","example":1000},"entries":{"description":"Recent ledger entries, newest first.","type":"array","items":{"$ref":"#/components/schemas/CreditLedgerEntryDto"}}},"required":["balance_steps","low_balance","low_balance_threshold_steps","entries"],"x-pc-sdk-model":"Credits"},"StatsWindow":{"type":"string","enum":["1d","7d","30d"]},"TrawlStatsTotalsDto":{"type":"object","properties":{"jobs":{"type":"number","description":"Jobs in the workspace, all time, in any status.","example":418},"balance_steps":{"type":"number","description":"Remaining Trawl steps: the sum of the credit ledger, the same number `GET /v1/workspaces/{workspaceId}/credits` returns. Zero before the first job; can be slightly negative after the last job of a budget.","example":8650}},"required":["jobs","balance_steps"],"x-pc-sdk-model":"TrawlStatsTotals"},"TrawlStatsInWindowDto":{"type":"object","properties":{"jobs":{"type":"number","description":"Jobs created in the window, in any status.","example":37},"succeeded":{"type":"number","description":"Of those, jobs that succeeded.","example":33},"failed":{"type":"number","description":"Of those, jobs that failed.","example":3},"steps_used":{"type":"number","description":"Steps consumed by the jobs created in the window. Jobs still running, and jobs from before metering, add nothing.","example":512}},"required":["jobs","succeeded","failed","steps_used"],"x-pc-sdk-model":"TrawlStatsInWindow"},"TrawlStatsMetric":{"type":"string","enum":["jobs"]},"StatsGranularity":{"type":"string","enum":["day"]},"TrawlStatsSeriesPointDto":{"type":"object","properties":{"date":{"type":"string","description":"UTC calendar day.","format":"date","example":"2026-10-07"},"value":{"type":"number","description":"Value for that day.","example":4}},"required":["date","value"],"x-pc-sdk-model":"TrawlStatsSeriesPoint"},"TrawlStatsSeriesDto":{"type":"object","properties":{"metric":{"description":"Jobs created per UTC day in the workspace, in any status.","example":"jobs","allOf":[{"$ref":"#/components/schemas/TrawlStatsMetric"}]},"granularity":{"description":"Bucket width.","example":"day","allOf":[{"$ref":"#/components/schemas/StatsGranularity"}]},"points":{"description":"Dense and zero-filled: one point per UTC day in the window, oldest first, ending today.","type":"array","items":{"$ref":"#/components/schemas/TrawlStatsSeriesPointDto"}}},"required":["metric","granularity","points"],"x-pc-sdk-model":"TrawlStatsSeries"},"TrawlWorkspaceStatsDto":{"type":"object","properties":{"window":{"description":"Window the counts cover.","example":"30d","allOf":[{"$ref":"#/components/schemas/StatsWindow"}]},"generated_at":{"type":"string","format":"date-time","description":"When these numbers were computed. Responses are cached for up to 60 seconds.","example":"2026-10-07T09:00:00.000Z"},"last_activity_at":{"type":"string","format":"date-time","nullable":true,"description":"When the workspace's most recent job was created. Null when there has never been one.","example":"2026-10-07T08:41:12.000Z"},"totals":{"description":"All-time counts and the credit balance.","allOf":[{"$ref":"#/components/schemas/TrawlStatsTotalsDto"}]},"in_window":{"description":"Counts for the window.","allOf":[{"$ref":"#/components/schemas/TrawlStatsInWindowDto"}]},"series":{"description":"Daily jobs.","allOf":[{"$ref":"#/components/schemas/TrawlStatsSeriesDto"}]}},"required":["window","generated_at","last_activity_at","totals","in_window","series"],"x-pc-sdk-model":"TrawlWorkspaceStats"},"ErrorParamDto":{"type":"object","properties":{"param":{"type":"string","description":"Wire path of the invalid field: `email`, `items.0.display_name`.","example":"email"},"message":{"type":"string","description":"What is wrong with it; the same text as the field's entry in an array `message`.","example":"email must be an email"},"code":{"type":"string","description":"The failed rule (class-validator constraint name, e.g. `isEmail`, `isUuid`, `whitelistValidation` for a field the operation does not declare), or `invalid`.","example":"isEmail"}},"required":["param","message","code"]},"ErrorResponseDto":{"type":"object","properties":{"status_code":{"type":"number","description":"HTTP status code, same as the response status.","example":404},"statusCode":{"type":"number","description":"Deprecated alias of `status_code`, same value. Kept so existing clients keep working; read `status_code`. Error responses carry `Deprecation: true` while it is sent.","example":404,"deprecated":true},"message":{"description":"Human-readable error message. A string, or on a `VALIDATION_ERROR` an array with one message per invalid field (the same texts as `errors[].message`). Branch on `code`, not on this.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"example":"Domain not found"},"code":{"description":"Stable, machine-readable error code; branch on this, not on `message`. Always present. A route-specific code when the route defines one, otherwise the generic code for the status (`NOT_FOUND`, `VALIDATION_ERROR`, `RATE_LIMITED`, ...). Open set: accept codes you do not know. Each code is documented at https://productcraft.co/docs/errors#<code>.","example":"NOT_FOUND","allOf":[{"$ref":"#/components/schemas/TrawlErrorCode"}]},"request_id":{"type":"string","description":"The request's id, same as the `X-Request-Id` response header. Quote it when contacting support.","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$","example":"req_6f1c2a9d4b7e4f0a8c3d2e1f0a9b8c7d"},"errors":{"description":"One entry per invalid request field on a `VALIDATION_ERROR`; empty on every other error.","type":"array","items":{"$ref":"#/components/schemas/ErrorParamDto"}},"details":{"type":"object","description":"Structured context for errors a client has to act on, keyed in snake_case and specific to the code (e.g. `{ fqdn, reason }` on `from_domain_not_verified`). Absent on most errors.","additionalProperties":true,"example":{"fqdn":"mail.example.com","reason":"not_verified"}}},"required":["status_code","message","code","request_id","errors"]},"TrawlErrorCode":{"description":"Error codes this API returns (open set: a client must accept codes it does not know; new codes are not a breaking change). Each is documented at https://productcraft.co/docs/errors#<code>.","anyOf":[{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","PAYMENT_REQUIRED","FORBIDDEN","NOT_FOUND","METHOD_NOT_ALLOWED","NOT_ACCEPTABLE","REQUEST_TIMEOUT","CONFLICT","GONE","PRECONDITION_FAILED","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE_ENTITY","PRECONDITION_REQUIRED","RATE_LIMITED","INTERNAL_SERVER_ERROR","NOT_IMPLEMENTED","BAD_GATEWAY","SERVICE_UNAVAILABLE","GATEWAY_TIMEOUT","SERVICE_NOT_ENABLED","INVALID_CREDENTIALS","SESSION_REVOKED","MCP_NOT_PERMITTED","MCP_WORKSPACE_MISMATCH","PAK_NOT_PERMITTED","ACTIONS_NOT_GRANTABLE","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_KEY_REQUIRED","IDEMPOTENCY_IN_PROGRESS","WEBHOOK_ENDPOINT_LIMIT","WEBHOOK_ENDPOINT_NOT_FOUND","WEBHOOK_ENDPOINT_NOT_SUBSCRIBED","WEBHOOK_EVENT_NOT_FOUND","WEBHOOK_EVENT_TYPE_UNKNOWN","TOO_MANY_ACTIVE_JOBS","TRAWL_CREDITS_EXHAUSTED"]},{"type":"string","description":"A code added after this document was published."}],"x-pc-open-enum":true,"x-pc-error-docs":"https://productcraft.co/docs/errors#{code}","x-enum-descriptions":{"BAD_REQUEST":"The request is malformed (not valid JSON, a bad header, an unusable parameter).","VALIDATION_ERROR":"One or more request fields are invalid; `errors` lists each field (`param`, `message`, `code`).","UNAUTHORIZED":"No credentials, or the token or API key is invalid, expired or revoked.","PAYMENT_REQUIRED":"The workspace has no credit or plan for this operation.","FORBIDDEN":"The caller is authenticated but not allowed to do this.","NOT_FOUND":"The resource does not exist, or belongs to another workspace or app.","METHOD_NOT_ALLOWED":"The route does not accept this HTTP method.","NOT_ACCEPTABLE":"The route cannot produce the requested content type.","REQUEST_TIMEOUT":"The request took too long to arrive.","CONFLICT":"The request conflicts with the current state of the resource.","GONE":"The resource existed but has been removed for good.","PRECONDITION_FAILED":"A precondition header did not match.","PAYLOAD_TOO_LARGE":"The request body is over the size limit.","UNSUPPORTED_MEDIA_TYPE":"The request body is not in a content type the route accepts.","UNPROCESSABLE_ENTITY":"The request is well-formed but cannot be carried out.","PRECONDITION_REQUIRED":"The route requires a precondition header.","RATE_LIMITED":"Too many requests; retry after the `Retry-After` header.","INTERNAL_SERVER_ERROR":"An unexpected server error. Retrying is safe for idempotent requests; quote `request_id` to support.","NOT_IMPLEMENTED":"The server does not support this operation.","BAD_GATEWAY":"An upstream service returned an invalid response.","SERVICE_UNAVAILABLE":"The service or one of its dependencies is temporarily unavailable; retry.","GATEWAY_TIMEOUT":"An upstream service did not answer in time; retry.","SERVICE_NOT_ENABLED":"The workspace has not enabled this product.","INVALID_CREDENTIALS":"The credentials were not accepted.","SESSION_REVOKED":"The session behind this token was signed out or revoked.","MCP_NOT_PERMITTED":"An MCP (agent) token cannot call this route.","MCP_WORKSPACE_MISMATCH":"The MCP token was issued for a different workspace.","PAK_NOT_PERMITTED":"A workspace API key cannot call this route.","ACTIONS_NOT_GRANTABLE":"The policy grants actions the caller does not hold.","IDEMPOTENCY_KEY_REUSE":"The `Idempotency-Key` was already used with a different body or route.","IDEMPOTENCY_KEY_REQUIRED":"This operation requires an `Idempotency-Key` header.","IDEMPOTENCY_IN_PROGRESS":"A request with the same `Idempotency-Key` is still running. Retry after `Retry-After` seconds with the same key to get its response.","WEBHOOK_ENDPOINT_LIMIT":"The workspace has the maximum number of webhook endpoints.","WEBHOOK_ENDPOINT_NOT_FOUND":"No webhook endpoint with this id in this workspace.","WEBHOOK_ENDPOINT_NOT_SUBSCRIBED":"The endpoint is not subscribed to this event type.","WEBHOOK_EVENT_NOT_FOUND":"No webhook event with this id in this workspace.","WEBHOOK_EVENT_TYPE_UNKNOWN":"The event type is not one this product sends.","TOO_MANY_ACTIVE_JOBS":"The workspace has the maximum number of active jobs; wait or cancel some.","TRAWL_CREDITS_EXHAUSTED":"The workspace has no Trawl credits left."}}},"headers":{"X-RateLimit-Limit-short":{"description":"Requests allowed per second from this client IP (20).","schema":{"type":"integer"}},"X-RateLimit-Remaining-short":{"description":"Requests left in the current one-second window.","schema":{"type":"integer"}},"X-RateLimit-Reset-short":{"description":"Seconds until the one-second window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit-long":{"description":"Requests allowed per minute from this client IP (600).","schema":{"type":"integer"}},"X-RateLimit-Remaining-long":{"description":"Requests left in the current one-minute window.","schema":{"type":"integer"}},"X-RateLimit-Reset-long":{"description":"Seconds until the one-minute window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying. Sent on every 429.","schema":{"type":"integer"}},"Retry-After-short":{"description":"Seconds to wait, when the per-second IP limit tripped.","schema":{"type":"integer"}},"Retry-After-long":{"description":"Seconds to wait, when the per-minute IP limit tripped.","schema":{"type":"integer"}},"RequestId":{"description":"The request's id: the inbound `X-Request-Id` when it is 1 to 128 characters of `A-Z a-z 0-9 . _ : -`, otherwise a fresh `req_...` id. Error bodies repeat it as `request_id`. Quote it when contacting support.","schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$","example":"req_6f1c2a9d4b7e4f0a8c3d2e1f0a9b8c7d"}}}}}