From 00f2b8478a3a476f85775a66d0b003794d94026a Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 10:00:24 +0000 Subject: [PATCH 1/2] fix(openapi): Autofix OpenAPI spec validation errors from production log Fix 1: Webhook object no longer requires requestUrl Error: Jul 16 10:34:11 apify-api SOFT_FAIL {"errors":[{"errorCode":"required.openapi.validation","message":"must have required property 'requestUrl'","path":"/response/data/requestUrl"}],"method":"GET","msg":"Response OpenAPI validation error","statusCode":200.0,"url":"/v2/webhooks/{webhookId}"} Files: apify-api/openapi/components/schemas/webhooks/Webhook.yaml:2 Root cause: requestUrl is optional in apify-core and enforced only for webhooks with actionType HTTP_REQUEST. Webhooks with other action types (Slack message, Google Mail, Google Drive, GitHub issue) are stored without requestUrl, and the API response projection (lodash pick) omits missing keys, so GET /v2/webhooks/{webhookId} legitimately returns a webhook object without requestUrl. Reference: https://github.com/apify/apify-core/tree/446d6faa5a503ddb64447c4b6b758a811b11b397/src/packages/schemas/src/webhooks.ts#L315 Fix 2: Webhook summary in webhook dispatch allows null requestUrl Error: Jul 21 03:20:22 apify-api SOFT_FAIL {"errors":[{"errorCode":"type.openapi.validation","message":"must be string","path":"/response/data/webhook/requestUrl"},{"errorCode":"type.openapi.validation","message":"must be null","path":"/response/data/webhook"},{"errorCode":"anyOf.openapi.validation","message":"must match a schema in anyOf","path":"/response/data/webhook"}],"method":"POST","msg":"Response OpenAPI validation error","statusCode":201.0,"url":"/v2/webhooks/{webhookId}/test?token="} Files: apify-api/openapi/components/schemas/webhook-dispatches/WebhookDispatchWebhookSummary.yaml:10 Root cause: POST /v2/webhooks/{webhookId}/test embeds the stored webhook into the created dispatch and the response projection includes webhook.requestUrl. Webhook create/update accepts requestUrl as nullish, so non-HTTP webhooks can be stored with an explicit null requestUrl, which is then returned inside the dispatch's webhook summary. Reference: https://github.com/apify/apify-core/tree/446d6faa5a503ddb64447c4b6b758a811b11b397/src/packages/zod-types/src/common/webhooks.ts#L57 Fix 3: Add 402 response to POST /v2/actor-tasks/{actorTaskId}/run-sync Error: Jul 20 15:56:30 apify-api SOFT_FAIL {"errors":[{"message":"no schema defined for status code '402' in the openapi spec","path":"/v2/actor-tasks/{actorTaskId}/run-sync?timeout=300&memory=1024&maxItems=20&build=latest&outputRecordKey=OUTPUT&webhooks="}],"method":"POST","msg":"Response OpenAPI validation error","statusCode":402.0,"url":"/v2/actor-tasks/{actorTaskId}/run-sync?timeout=300&memory=1024&maxItems=20&build=latest&outputRecordKey=OUTPUT&webhooks="} Files: apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@run-sync.yaml:144 Root cause: Launching a task run enforces platform usage limits which throw 402 Payment Required errors (actor memory limit, concurrent runs limit, not enough usage to run paid Actor). Sibling run-sync endpoints already document 402 via the shared PaymentRequired response component. Reference: https://github.com/apify/apify-core/tree/446d6faa5a503ddb64447c4b6b758a811b11b397/src/packages/errors/src/errors/actor.ts#L5 Fix 4: Add 408 response to POST /v2/actor-tasks/{actorTaskId}/run-sync Error: Jul 21 18:01:10 apify-api SOFT_FAIL {"errors":[{"message":"no schema defined for status code '408' in the openapi spec","path":"/v2/actor-tasks/{actorTaskId}/run-sync?token=&outputRecordKey=OUTPUT"}],"method":"POST","msg":"Response OpenAPI validation error","statusCode":408.0,"url":"/v2/actor-tasks/{actorTaskId}/run-sync?token=&outputRecordKey=OUTPUT"} Files: apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@run-sync.yaml:152 Root cause: The run-sync handler throws run-timeout-exceeded with status 408 when the run does not reach a terminal status within the maximum synchronous wait time. The GET operation of the same path and all sibling run-sync endpoints already document 408 via the shared Timeout response component. Reference: https://github.com/apify/apify-core/tree/446d6faa5a503ddb64447c4b6b758a811b11b397/src/api/src/routes/actor_tasks/run_sync.ts#L68 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01SdZCyJ9vhtU2DKVvQYiNUD --- .../webhook-dispatches/WebhookDispatchWebhookSummary.yaml | 3 ++- apify-api/openapi/components/schemas/webhooks/Webhook.yaml | 2 +- .../paths/actor-tasks/actor-tasks@{actorTaskId}@run-sync.yaml | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apify-api/openapi/components/schemas/webhook-dispatches/WebhookDispatchWebhookSummary.yaml b/apify-api/openapi/components/schemas/webhook-dispatches/WebhookDispatchWebhookSummary.yaml index 4a65ed216b..8c96517630 100644 --- a/apify-api/openapi/components/schemas/webhook-dispatches/WebhookDispatchWebhookSummary.yaml +++ b/apify-api/openapi/components/schemas/webhook-dispatches/WebhookDispatchWebhookSummary.yaml @@ -8,8 +8,9 @@ properties: condition: $ref: ../webhooks/WebhookCondition.yaml requestUrl: - type: string + type: [string, "null"] format: uri + description: URL of the HTTP request sent by the webhook. It is `null` for webhooks with a non-HTTP action type (e.g. Slack or email notifications). examples: ["https://example.com/webhook"] isAdHoc: type: boolean diff --git a/apify-api/openapi/components/schemas/webhooks/Webhook.yaml b/apify-api/openapi/components/schemas/webhooks/Webhook.yaml index 6ef6faf7c8..84b960dd90 100644 --- a/apify-api/openapi/components/schemas/webhooks/Webhook.yaml +++ b/apify-api/openapi/components/schemas/webhooks/Webhook.yaml @@ -7,7 +7,6 @@ required: - eventTypes - condition - ignoreSslErrors - - requestUrl type: object properties: id: @@ -47,6 +46,7 @@ properties: requestUrl: type: [string, "null"] format: uri + description: URL of the HTTP request sent by the webhook. It is omitted or `null` for webhooks with a non-HTTP action type (e.g. Slack or email notifications). examples: ["http://example.com/"] payloadTemplate: type: [string, "null"] diff --git a/apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@run-sync.yaml b/apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@run-sync.yaml index e27d4040d4..7a87b54e72 100644 --- a/apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@run-sync.yaml +++ b/apify-api/openapi/paths/actor-tasks/actor-tasks@{actorTaskId}@run-sync.yaml @@ -141,12 +141,16 @@ post: $ref: ../../components/responses/BadRequest.yaml "401": $ref: ../../components/responses/Unauthorized.yaml + "402": + $ref: ../../components/responses/PaymentRequired.yaml "403": $ref: ../../components/responses/Forbidden.yaml "404": $ref: ../../components/responses/NotFound.yaml "405": $ref: ../../components/responses/MethodNotAllowed.yaml + "408": + $ref: ../../components/responses/Timeout.yaml "413": $ref: ../../components/responses/PayloadTooLarge.yaml "415": From 3c3397ffaab1d44c5d493ef3c5ae7b5cedb700a7 Mon Sep 17 00:00:00 2001 From: Josef Prochazka Date: Wed, 22 Jul 2026 14:17:52 +0200 Subject: [PATCH 2/2] Describe better `requestUrl` for hook actions that are not webhooks --- .../webhook-dispatches/WebhookDispatchWebhookSummary.yaml | 2 +- apify-api/openapi/components/schemas/webhooks/Webhook.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apify-api/openapi/components/schemas/webhook-dispatches/WebhookDispatchWebhookSummary.yaml b/apify-api/openapi/components/schemas/webhook-dispatches/WebhookDispatchWebhookSummary.yaml index 8c96517630..7890b26fd4 100644 --- a/apify-api/openapi/components/schemas/webhook-dispatches/WebhookDispatchWebhookSummary.yaml +++ b/apify-api/openapi/components/schemas/webhook-dispatches/WebhookDispatchWebhookSummary.yaml @@ -10,7 +10,7 @@ properties: requestUrl: type: [string, "null"] format: uri - description: URL of the HTTP request sent by the webhook. It is `null` for webhooks with a non-HTTP action type (e.g. Slack or email notifications). + description: URL of the HTTP request sent by the webhook. It is `null` for hook actions other than the conventional HTTP case (e.g. Slack or email notifications). examples: ["https://example.com/webhook"] isAdHoc: type: boolean diff --git a/apify-api/openapi/components/schemas/webhooks/Webhook.yaml b/apify-api/openapi/components/schemas/webhooks/Webhook.yaml index 84b960dd90..679bdd5bf1 100644 --- a/apify-api/openapi/components/schemas/webhooks/Webhook.yaml +++ b/apify-api/openapi/components/schemas/webhooks/Webhook.yaml @@ -46,7 +46,7 @@ properties: requestUrl: type: [string, "null"] format: uri - description: URL of the HTTP request sent by the webhook. It is omitted or `null` for webhooks with a non-HTTP action type (e.g. Slack or email notifications). + description: URL of the HTTP request sent by the webhook. It is omitted or `null` for hook actions other than the conventional HTTP case (e.g. Slack or email notifications). examples: ["http://example.com/"] payloadTemplate: type: [string, "null"]