Gymterview
middle

Что такое Webhooks в Jira и как их использовать?

Webhook — механизм уведомления внешних систем о событиях в Jira через HTTP POST-запросы. Когда в Jira происходит определённое событие, Jira отправляет JSON-payload на указанный URL.

Аналогия из жизни: webhook — это как SMS-уведомление от банка. Вместо того чтобы каждые 5 минут проверять баланс (polling), банк сам отправляет вам сообщение при каждой операции (push).

Доступные события

Событие Описание
jira:issue_created Создание задачи
jira:issue_updated Обновление задачи
jira:issue_deleted Удаление задачи
jira:worklog_updated Обновление worklog
sprint_created Создание спринта
sprint_started Старт спринта
sprint_closed Закрытие спринта
board_created Создание доски
project_created Создание проекта
comment_created Добавление комментария
issuelink_created Создание связи

Регистрация webhook через REST API

Пример
public void registerWebhook(String jiraBaseUrl, String token) {
    String body = """
            {
                "name": "My Integration Webhook",
                "url": "https://my-app.example.com/api/jira/webhook",
                "events": [
                    "jira:issue_created",
                    "jira:issue_updated"
                ],
                "filters": {
                    "issue-related-events-section": "project = PROJ AND type = Bug"
                },
                "excludeBody": false
            }
            """;

    restClient.post()
            .uri(jiraBaseUrl + "/rest/webhooks/1.0/webhook")
            .header("Authorization", "Bearer " + token)
            .body(body)
            .retrieve()
            .toBodilessEntity();
}

Структура payload (issue_updated)

Пример
{
    "timestamp": 1700000000000,
    "webhookEvent": "jira:issue_updated",
    "issue_event_type_name": "issue_generic",
    "user": {
        "accountId": "5a1234567890",
        "displayName": "Ivan Petrov"
    },
    "issue": {
        "key": "PROJ-123",
        "fields": {
            "summary": "Исправить баг авторизации",
            "status": {"name": "In Progress"},
            "assignee": {"displayName": "Ivan Petrov"},
            "priority": {"name": "Critical"}
        }
    },
    "changelog": {
        "items": [
            {
                "field": "status",
                "fromString": "Open",
                "toString": "In Progress"
            }
        ]
    }
}

Spring Boot webhook listener

Код JiraWebhookController
@RestController
@RequestMapping("/api/jira/webhook")
public class JiraWebhookController {

    private static final Logger log = LoggerFactory.getLogger(JiraWebhookController.class);

    @PostMapping
    public ResponseEntity<Void> handleWebhook(
            @RequestBody Map<String, Object> payload,
            @RequestHeader(value = "X-Atlassian-Webhook-Identifier",
                           required = false) String webhookId) {

        String event = (String) payload.get("webhookEvent");
        log.info("Получен webhook: event={}, id={}", event, webhookId);

        switch (event) {
            case "jira:issue_created" -> handleIssueCreated(payload);
            case "jira:issue_updated" -> handleIssueUpdated(payload);
            default -> log.warn("Неизвестное событие: {}", event);
        }

        return ResponseEntity.ok().build();
    }

    private void handleIssueCreated(Map<String, Object> payload) {
        Map<String, Object> issue = (Map<String, Object>) payload.get("issue");
        String key = (String) issue.get("key");
        Map<String, Object> fields = (Map<String, Object>) issue.get("fields");
        String summary = (String) fields.get("summary");
        log.info("Создана задача: {} - {}", key, summary);
    }

    private void handleIssueUpdated(Map<String, Object> payload) {
        Map<String, Object> changelog = (Map<String, Object>) payload.get("changelog");
        if (changelog != null) {
            List<Map<String, Object>> items =
                    (List<Map<String, Object>>) changelog.get("items");
            for (Map<String, Object> item : items) {
                if ("status".equals(item.get("field"))) {
                    log.info("Статус изменён: {} → {}",
                            item.get("fromString"), item.get("toString"));
                }
            }
        }
    }
}

Безопасность

  • Shared Secret (DC): при регистрации webhook указывается secret, Jira подписывает payload HMAC-SHA256
  • IP Whitelisting: ограничение входящих запросов по IP Jira-инстанса
  • HTTPS: обязательно для production
  • Идемпотентность: webhook может быть доставлен повторно — обработчик должен быть идемпотентным

Частые ошибки

  • Синхронная тяжёлая обработка в webhook-обработчике — нужно принять webhook, ответить 200 и обработать асинхронно
  • Отсутствие валидации подписи — любой может отправить поддельный webhook
  • Нет обработки дубликатов — Jira может повторно отправить webhook при timeout
  • Регистрация webhook без JQL-фильтра — получение всех событий инстанса перегружает приложение

Как используется в 2026

  • В Cloud рекомендуется использовать Forge Triggers вместо прямой регистрации webhooks
  • Jira Automation (встроенная) заменяет простые webhook-сценарии (if event, then action)
  • Для DC webhooks остаются основным механизмом push-интеграции
  • Тренд на event-driven архитектуру: Jira webhook -> Kafka -> микросервисы

На собеседовании: подчеркните, что webhook — push-модель в отличие от polling. Обязательно упомяните идемпотентность и асинхронную обработку. Webhook должен ответить за 10 секунд (DC) / 30 секунд (Cloud). Для Cloud упомяните Forge Triggers как более надёжную альтернативу.