{"record":{"id":"b4d8c1fcad8c27ed","repo":"Significant-Gravitas/AutoGPT","slug":"webhook-webhook-id-not-found","errorCode":null,"errorMessage":"Webhook #{webhook_id} not found","messagePattern":"Webhook #(.+?) not found","errorType":"exception","errorClass":"NotFoundError","httpStatus":404,"severity":"warning","filePath":"autogpt_platform/backend/backend/api/features/integrations/router.py","lineNumber":706,"sourceCode":"    provider: Annotated[\n        ProviderName, Path(title=\"Provider where the webhook was registered\")\n    ],\n    webhook_id: Annotated[str, Path(title=\"Our ID for the webhook\")],\n):\n    logger.debug(f\"Received {provider.value} webhook ingress for ID {webhook_id}\")\n    webhook_manager = get_webhook_manager(provider)\n    try:\n        webhook = await get_webhook(webhook_id, include_relations=True)\n        # Sanity check: `provider` from URL and fetched webhook must match.\n        # Otherwise the URL provider's verifier runs instead of the webhook's\n        # own (a no-op for unsigned providers like Compass), bypassing it.\n        if webhook.provider.value.lower() != provider.value.lower():\n            logger.warning(\n                f\"Webhook #{webhook_id} provider mismatch: \"\n                f\"registered as {webhook.provider.value}, ingress via {provider.value}\"\n            )\n            # Same as the actual \"webhook not found\" response to conceal existence\n            raise NotFoundError(f\"Webhook #{webhook_id} not found\")\n    except NotFoundError as e:\n        logger.warning(f\"Webhook payload received for unknown webhook #{webhook_id}\")\n        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n    logger.debug(f\"Webhook #{webhook_id}: {webhook}\")\n\n    user_id = webhook.user_id\n    try:\n        credentials = (\n            await creds_manager.get(user_id, webhook.credentials_id)\n            if webhook.credentials_id\n            else None\n        )\n    except NotFoundError as e:\n        logger.warning(f\"Webhook payload received for unknown webhook #{webhook_id}\")\n        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n\n    # Run provider signature verification (no-op for providers whose protocol\n    # has no signing scheme). 403 on failure; not 404 — that would leak","sourceCodeStart":688,"sourceCodeEnd":724,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/backend/backend/api/features/integrations/router.py#L688-L724","documentation":"Raised by the webhook ingress endpoint (POST /integrations/{provider}/webhooks/{webhook_id}/webhook) when no webhook with the given ID exists in the database, or deliberately when the webhook exists but its registered provider does not match the provider in the URL. The identical 404 for both cases conceals which webhook IDs exist, preventing enumeration. It surfaces as HTTP 404 with detail 'Webhook #{webhook_id} not found'.","triggerScenarios":"POSTing a webhook event to a URL whose webhook_id was deleted or pruned (dangling webhooks are pruned when credentials are removed), replaying an old URL after re-registering the webhook, or sending the event through the wrong provider segment of the URL (e.g. hitting /integrations/github/webhooks/{id}/webhook for a webhook registered under compass), which triggers the provider-mismatch sanity check that intentionally returns this same 404.","commonSituations":"Provider removed the webhook on their side and a queued event still arrives after AutoGPT pruned it; developer copied the ingress URL with the wrong provider name; webhook was auto-pruned because its credentials were deleted; stale hardcoded URL in a test suite after the webhook table was reset.","solutions":["Verify the webhook still exists: GET /integrations/webhooks (list webhooks for the user) and confirm the ID and its provider field.","Check the provider segment of the ingress URL matches the webhook's registered provider exactly (the check is case-insensitive on provider value).","If the webhook was pruned, re-register it via the provider block or POST /integrations/{provider}/webhooks and update the URL configured at the provider.","If you control the sender, stop retrying on 404 — the resource is gone and retries will never succeed."],"exampleFix":"# before: sending event via wrong provider path\nPOST /integrations/github/webhooks/9d2f.../webhook  # webhook is registered under 'compass'\n\n# after: match the webhook's registered provider\nPOST /integrations/compass/webhooks/9d2f.../webhook","handlingStrategy":"validation","validationCode":"# Before relying on an ingress URL, confirm the webhook exists and provider matches\nwebhooks = await client.get(\"/integrations/webhooks\")\nwh = next((w for w in webhooks.json() if w[\"id\"] == webhook_id), None)\nif wh is None or wh[\"provider\"].lower() != provider.lower():\n    re_register_webhook(provider)  # stale URL — rebuild it before the provider sends events","typeGuard":null,"tryCatchPattern":"# Ingress endpoints are called by providers, not your code — do not retry 404.\n# If you proxy ingress, classify:\nif resp.status_code == 404:\n    disable_webhook_url(webhook_id)  # terminal: webhook gone or provider mismatch\nelif resp.status_code == 403:\n    alert_signature_problem(webhook_id)\n","preventionTips":["Store the webhook ID together with its provider when registering; always build the ingress URL from that pair.","Treat 404 from ingress as terminal and stop provider-side retries.","Re-register webhooks after deleting/re-creating credentials rather than reusing old URLs."],"tags":["webhook","http-404","integrations","provider-mismatch"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}