Skip to content

Webhooks

Active Development

Webhook payload formats are determined by the source platform (GitHub, GitLab, etc.) and are not controlled by Aether. The information here reflects Aether's current handling logic, which may evolve as integrations are updated.

Unified Webhook Endpoint

All webhooks are received at a single endpoint:

POST /webhook/:slug

The :slug identifies the source. For built-in integrations, use:

SourceSlugURL
GitHubgithubPOST /webhook/github
GitLabgitlabPOST /webhook/gitlab
JirajiraPOST /webhook/jira
PlaneplanePOST /webhook/plane

For dynamic integrations, the slug is the identifier you assign when creating the integration.

How Webhooks Are Processed

Incoming webhook


1. Verify signature / token (if configured)


2. Persist raw webhook event to database


3. Extract source + event type + action from payload


4. Look up canonical trigger from source_event_mappings table


5. If no mapping found → log and discard
   If mapping found → look up tasks from trigger_task_mappings


6. For each task → call agent via LLM → post response to tracker

Unhandled events (no mapping) are silently discarded with a log entry. No error is returned to the sender.


Security Verification

GitHub

GitHub signs payloads with HMAC-SHA256. Aether verifies the X-Hub-Signature-256 header against the secret you configure when setting up the webhook in GitHub.

  • If no secret is configured on the GitHub side, Aether accepts unsigned payloads (not recommended for production).

GitLab

GitLab sends a secret token in the X-Gitlab-Token header. Configure the same token in both GitLab's webhook settings and Aether's GITLAB_TOKEN.

Jira

Jira webhooks do not include built-in signing. Aether accepts all payloads at the Jira endpoint. Use network-level controls (firewall, IP allowlist) to restrict access.

Plane

Plane sends the API key in the X-API-Key header. Aether validates it against PLANE_API_KEY.


GitHub Payloads

Required headers:

Content-Type: application/json
X-GitHub-Event: <event-name>
X-Hub-Signature-256: sha256=<hmac>  (if secret configured)

Issues Event

json
{
  "action": "opened",
  "issue": {
    "number": 456,
    "title": "Bug in login flow",
    "body": "When I click login, nothing happens.",
    "state": "open",
    "user": { "login": "dev-user" },
    "labels": [{ "name": "bug" }]
  },
  "repository": {
    "full_name": "owner/repo"
  }
}

Handled actions: opened, edited, closed, reopened, labeled, unlabeled

Pull Request Event

json
{
  "action": "opened",
  "pull_request": {
    "number": 123,
    "title": "Add OAuth support",
    "body": "Implements OAuth 2.0 login.",
    "state": "open",
    "head": { "ref": "feature/oauth" },
    "base": { "ref": "main" }
  },
  "repository": {
    "full_name": "owner/repo"
  }
}

Handled actions: opened, edited, closed, reopened, ready_for_review, synchronize

Issue Comment Event

json
{
  "action": "created",
  "issue": { "number": 456 },
  "comment": {
    "id": 999,
    "body": "Can you provide steps to reproduce?"
  },
  "repository": {
    "full_name": "owner/repo"
  }
}

GitLab Payloads

Required headers:

Content-Type: application/json
X-Gitlab-Event: <hook-type>
X-Gitlab-Token: <secret>  (if configured)

Issue Hook

json
{
  "object_kind": "issue",
  "event_type": "issue",
  "user": { "username": "dev-user" },
  "project": {
    "id": 123,
    "path_with_namespace": "group/project"
  },
  "object_attributes": {
    "iid": 42,
    "title": "Performance regression in API",
    "description": "Response times increased by 300ms",
    "state": "opened",
    "action": "open"
  }
}

Handled actions: open, close, reopen, update

Merge Request Hook

json
{
  "object_kind": "merge_request",
  "event_type": "merge_request",
  "user": { "username": "dev-user" },
  "project": {
    "id": 123,
    "path_with_namespace": "group/project"
  },
  "object_attributes": {
    "iid": 55,
    "title": "Fix memory leak in worker pool",
    "description": "Adds proper cleanup on shutdown",
    "state": "opened",
    "action": "open",
    "source_branch": "fix/memory-leak",
    "target_branch": "main"
  }
}

Jira Payloads

Issue Created

json
{
  "webhookEvent": "jira:issue_created",
  "issue": {
    "key": "PROJ-123",
    "fields": {
      "summary": "Login fails for SSO users",
      "description": "Users with SSO enabled cannot log in.",
      "issuetype": { "name": "Bug" },
      "priority": { "name": "High" },
      "status": { "name": "Open" }
    }
  }
}

Issue Updated

json
{
  "webhookEvent": "jira:issue_updated",
  "issue": {
    "key": "PROJ-123",
    "fields": {
      "summary": "Login fails for SSO users",
      "status": { "name": "In Progress" }
    }
  },
  "changelog": {
    "items": [
      {
        "field": "status",
        "fromString": "Open",
        "toString": "In Progress"
      }
    ]
  }
}

Comment Created

json
{
  "webhookEvent": "comment_created",
  "comment": {
    "id": "12345",
    "body": "Reproduced on version 2.3.1.",
    "author": { "displayName": "QA Engineer" }
  },
  "issue": { "key": "PROJ-123" }
}

Plane Payloads

Issue Created

json
{
  "event": "issue.created",
  "data": {
    "id": "abc123def456",
    "name": "API rate limiting needed",
    "description": "We need to add rate limiting to the public API.",
    "state": "backlog",
    "priority": "high",
    "project_id": "project-uuid",
    "workspace_slug": "my-workspace"
  }
}

Issue Updated

json
{
  "event": "issue.updated",
  "data": {
    "id": "abc123def456",
    "name": "API rate limiting needed",
    "state": "in_progress",
    "changes": {
      "state": {
        "old": "backlog",
        "new": "in_progress"
      }
    }
  }
}

Comment Created

json
{
  "event": "issue.activity.created",
  "data": {
    "activity_type": "comment",
    "comment": "Added Redis-based rate limiting in PR #45",
    "issue_id": "abc123def456"
  }
}

Testing Webhooks

Use curl to send test webhooks without configuring the actual external service:

GitHub Issue Opened

bash
curl -X POST http://localhost:8000/webhook/github \
  -H "Content-Type: application/json" \
  -H "X-GitHub-Event: issues" \
  -d '{
    "action": "opened",
    "issue": {
      "number": 1,
      "title": "Test Issue",
      "body": "This is a test issue for verifying the webhook integration."
    },
    "repository": {
      "full_name": "test-org/test-repo"
    }
  }'

GitLab Issue Opened

bash
curl -X POST http://localhost:8000/webhook/gitlab \
  -H "Content-Type: application/json" \
  -H "X-Gitlab-Event: Issue Hook" \
  -d '{
    "object_kind": "issue",
    "object_attributes": {
      "iid": 1,
      "title": "Test GitLab Issue",
      "description": "Testing webhook integration",
      "action": "open"
    },
    "project": {
      "path_with_namespace": "test-group/test-project"
    }
  }'

Jira Issue Created

bash
curl -X POST http://localhost:8000/webhook/jira \
  -H "Content-Type: application/json" \
  -d '{
    "webhookEvent": "jira:issue_created",
    "issue": {
      "key": "TEST-1",
      "fields": {
        "summary": "Test Jira Issue",
        "description": "Testing webhook integration"
      }
    }
  }'

Plane Issue Created

bash
curl -X POST http://localhost:8000/webhook/plane \
  -H "Content-Type: application/json" \
  -d '{
    "event": "issue.created",
    "data": {
      "id": "test-issue-id",
      "name": "Test Plane Issue",
      "description": "Testing webhook integration",
      "state": "backlog",
      "priority": "medium"
    }
  }'

View Processing Logs

bash
docker-compose logs -f goway | grep -i webhook

Configuring Webhooks in External Services

GitHub

  1. Go to Repository Settings → Webhooks → Add webhook
  2. Payload URL: https://your-aether-host/webhook/github
  3. Content type: application/json
  4. Secret: Set a secret and match it in your Aether config (recommended for production)
  5. Events: Select the events you want — at minimum, "Issues" and "Pull requests"

GitLab

  1. Go to Project Settings → Webhooks → Add new webhook
  2. URL: https://your-aether-host/webhook/gitlab
  3. Secret token: Optional but recommended
  4. Trigger: Issues events, Merge request events, Comments

Jira

  1. Go to Jira Administration → System → WebHooks → Create a WebHook
  2. URL: https://your-aether-host/webhook/jira
  3. Events: jira:issue_created, jira:issue_updated, comment_created
  4. JQL filter: Optionally filter to specific projects

Plane

  1. Go to Project Settings → Members & Settings → Webhooks
  2. URL: https://your-aether-host/webhook/plane
  3. Events: issue.created, issue.updated, issue.activity.created

Troubleshooting

Webhook delivered but nothing happens:

  1. Check that a source_event_mapping exists for this source + event + action
  2. Check that a trigger_task_mapping exists for the canonical trigger
  3. Check Aether logs: docker-compose logs -f goway

Webhook not being received:

  1. Verify the URL is correct and publicly reachable
  2. Check for firewall or reverse proxy issues
  3. Review the "Recent Deliveries" in GitHub/GitLab webhook settings

Signature verification failing:

  1. Ensure the secret matches exactly on both sides
  2. Check for whitespace or encoding issues in the secret

Released under the MIT License.