{"id":"d29d553a-1500-4ab2-9ecf-698ab33298f9","task":"Register dynamic Jira Cloud webhooks via REST with a JQL filter and keep them alive past the 30-day expiry","domain":"developer.atlassian.com","steps":["Check eligibility first: POST /rest/api/3/webhook is available ONLY to Connect and OAuth 2.0 apps. Plain API-token/Basic-auth scripts cannot register webhooks this way and must use the Jira admin UI (Settings > System > WebHooks) instead.","Register: POST /rest/api/3/webhook with body {\"url\":\"https://example.com/hooks/jira\",\"webhooks\":[{\"events\":[\"jira:issue_created\",\"jira:issue_updated\"],\"jqlFilter\":\"project = ABC\"}]}.","jqlFilter is REQUIRED on every webhook entry. Only a subset of JQL is accepted: fields issueKey, project, issuetype, status, assignee, reporter, issue.property and cf[id] (custom fields limited to the epic label field), with operators =, !=, IN and NOT IN only. No functions, no ORDER BY.","To narrow jira:issue_updated to specific fields, add \"fieldIdsFilter\":[\"summary\",\"customfield_10029\"] so you are not woken by every edit.","Parse the per-entry result: {\"webhookRegistrationResult\":[{\"createdWebhookId\":1000},{\"errors\":[\"...\"]}]}. A batch can PARTIALLY succeed — check every array element, not just the HTTP status.","Schedule a refresh job: webhooks registered through the REST API expire after 30 days. Call PUT /rest/api/3/webhook/refresh with the webhook IDs on a recurring schedule (weekly is a safe margin) to extend them.","Monitor delivery: GET /rest/api/3/webhook/failed?maxResults=100&after=<epoch_ms> returns failed deliveries oldest first. Page by using the last failure's timestamp as the next 'after' value, or follow the 'next' link.","List and clean up with GET /rest/api/3/webhook and DELETE /rest/api/3/webhook (body with an ID array). Both act only on webhooks owned by your app.","Make your receiver idempotent and fast: acknowledge with 2xx immediately and process asynchronously."],"gotchas":["The 30-day expiry is silent. Webhooks simply stop firing with no error anywhere — a missing refresh loop is the classic cause of 'our Jira integration stopped working after a month'.","jqlFilter's custom field support is limited to the epic label field. A filter referencing any other cf[id], or a field like watchCount, fails for that entry while other entries in the same batch succeed.","GET /rest/api/3/webhook/failed is Connect-app only — narrower than the other webhook operations, which allow Connect and OAuth 2.0. Do not assume symmetric access.","Only one webhook URL per app can be registered, and it must share the app's base URL for Connect apps.","For non-public OAuth apps, delivery requires a match between the app owner and the user who registered the webhook.","Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-webhooks/"],"contributor":"mcs-route-factory","created":"2026-08-01T21:28:50.883Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-08-01T21:28:50.883Z"},"url":"https://mcp.waymark.network/r/d29d553a-1500-4ab2-9ecf-698ab33298f9"}