Webhook Setup
This guide covers how to configure Aether to receive webhooks from GitHub, Jira, and Plane. Aether uses a single dynamic endpoint for all webhook sources — no per-source routes are required.
How It Works
All webhooks are received at:
POST /webhook/:slugThe :slug is a short identifier (e.g., github-default) that maps to a Webhook Mapping record stored in PostgreSQL. Each mapping defines:
- Source key — which issue tracker this is (e.g.,
github,jira,plane) - Extraction rules — JSONPath / header rules that extract the
event,action, and issue context from the raw payload - Auth config — how to verify the webhook signature or token
When a webhook arrives:
- Aether looks up the mapping by slug
- Verifies the auth (HMAC signature, bearer token, etc.)
- Extracts
event + action→ constructs asource_eventstring (e.g.,issues:opened) - Normalises to a canonical trigger (e.g.,
ticket.created) via thesource_event_mappingstable - Resolves the tasks for that trigger
- Dispatches each task to its assigned agent via the LLM service (with tool calling)
Seeding Webhook Mappings
Webhook mappings are stored in PostgreSQL and can be seeded from YAML. Aether ships a default set in goway/seeding/webhook_mappings.yaml.
Seed the defaults
curl -X POST http://localhost:8000/api/webhook-mappings/seedResponse:
{"created": 5, "skipped": 0}The seed endpoint is idempotent — running it multiple times is safe.
YAML format
webhook_mappings:
- name: "GitHub Default"
slug: "github-default"
source_key: "github"
description: "Default mapping for GitHub webhooks"
is_active: true
extraction_rules:
event:
from_header: "X-GitHub-Event" # read event name from header
action:
from_path: "$.action" # read action from payload (JSONPath)
issue_context:
issue_id: "$.issue.number"
owner: "$.repository.owner.login"
repo: "$.repository.name"
auth_config:
type: "hmac"
secret: "your-github-webhook-secret"
header: "X-Hub-Signature-256"
algorithm: "sha256"GitHub
Register the webhook in GitHub
- Go to Settings → Webhooks in your repository (or organisation)
- Set the Payload URL to:
https://your-aether-host/webhook/github-default - Set Content type to
application/json - Choose Let me select individual events and enable at minimum:
IssuesIssue commentsPull requests
- (Recommended) Set a Secret and configure HMAC auth below
Extraction rules
| Field | Source | Path / Header |
|---|---|---|
event | Header | X-GitHub-Event |
action | Payload | $.action |
issue_id | Payload | $.issue.number |
owner | Payload | $.repository.owner.login |
repo | Payload | $.repository.name |
YAML config with HMAC auth
- name: "GitHub Default"
slug: "github-default"
source_key: "github"
is_active: true
extraction_rules:
event:
from_header: "X-GitHub-Event"
action:
from_path: "$.action"
issue_context:
issue_id: "$.issue.number"
owner: "$.repository.owner.login"
repo: "$.repository.name"
auth_config:
type: "hmac"
secret: "my-secret-token"
header: "X-Hub-Signature-256"
algorithm: "sha256"Canonical event mappings (GitHub → Aether)
| GitHub event | Action | Canonical trigger |
|---|---|---|
issues | opened | ticket.created |
issues | edited | ticket.updated |
issues | closed | ticket.state_changed |
pull_request | opened | vcs.request_opened |
pull_request | synchronize | vcs.request_updated |
Seed these mappings with:
curl -X POST http://localhost:8000/api/tasks/sources/seedJira
Register the webhook in Jira
- Go to System → Advanced → WebHooks (Jira Admin)
- Create a new webhook with URL:
https://your-aether-host/webhook/jira-default - Select the events you want to receive (at minimum
issue_created,issue_updated)
Extraction rules
| Field | Source | Path / Header |
|---|---|---|
event | Header | X-Jira-Event (fallback: $.webhookEvent) |
action | Payload | $.issue_event_type_name |
issue_id | Payload | $.issue.id |
issue_key | Payload | $.issue.key |
project_id | Payload | $.issue.fields.project.id |
project_key | Payload | $.issue.fields.project.key |
YAML config
- name: "Jira Default"
slug: "jira-default"
source_key: "jira"
is_active: true
extraction_rules:
event:
from_header: "X-Jira-Event"
fallback_path: "$.webhookEvent"
action:
from_path: "$.issue_event_type_name"
issue_context:
issue_id: "$.issue.id"
issue_key: "$.issue.key"
project_id: "$.issue.fields.project.id"
project_key: "$.issue.fields.project.key"
auth_config:
type: "none" # Jira Cloud does not support outbound HMAC; restrict by IP insteadJira Cloud
Jira Cloud does not support webhook secrets. Consider restricting inbound traffic to Atlassian's IP ranges at your load balancer or firewall level.
Canonical event mappings (Jira → Aether)
| Jira event | Canonical trigger |
|---|---|
jira:issue_created | ticket.created |
jira:issue_updated | ticket.updated |
jira:issue_deleted | ticket.deleted |
Plane
Register the webhook in Plane
- Go to Workspace Settings → Integrations → Webhooks
- Create a new webhook with URL:
https://your-aether-host/webhook/plane-default - Enable events:
Issue created,Issue updated,Issue deleted,State changed
Extraction rules
| Field | Source | Path / Header |
|---|---|---|
event | Header | X-Plane-Event (fallback: $.event) |
action | Payload | $.action |
issue_id | Payload | $.data.id |
project_id | Payload | $.data.project |
workspace | Payload | $.data.workspace |
YAML config
- name: "Plane Default"
slug: "plane-default"
source_key: "plane"
is_active: true
extraction_rules:
event:
from_header: "X-Plane-Event"
fallback_path: "$.event"
action:
from_path: "$.action"
issue_context:
issue_id: "$.data.id"
project_id: "$.data.project"
workspace: "$.data.workspace"
auth_config:
type: "none"Canonical event mappings (Plane → Aether)
| Plane event | Canonical trigger |
|---|---|
issue:created | ticket.created |
issue:updated | ticket.updated |
issue:deleted | ticket.deleted |
issue:state:changed | ticket.state_changed |
Auth Config Reference
| Type | Description | Required fields |
|---|---|---|
none | No verification — any request is accepted | — |
hmac | HMAC-SHA256 signature in a header | secret, header, algorithm |
bearer | Authorization: Bearer <token> header | token |
basic | HTTP Basic Auth | username, password |
header | Custom header with static value | header_name, header_value |
HMAC example
auth_config:
type: "hmac"
secret: "my-shared-secret"
header: "X-Hub-Signature-256"
algorithm: "sha256" # sha256 or sha1Bearer example
auth_config:
type: "bearer"
token: "my-static-bearer-token"Full Seeding Sequence
Run these commands after a fresh Aether install to seed all default configuration:
# 1. Seed webhook mappings (GitHub, Jira, Plane, GitLab)
curl -X POST http://localhost:8000/api/webhook-mappings/seed
# 2. Seed task definitions
curl -X POST http://localhost:8000/api/tasks/seed
# 3. Seed canonical trigger → task mappings
curl -X POST http://localhost:8000/api/tasks/triggers/seed
# 4. Seed source event → canonical trigger mappings
curl -X POST http://localhost:8000/api/tasks/sources/seedAll seed endpoints return {"created": N, "skipped": M} and are idempotent.
Managing Mappings via API
You can also create and update webhook mappings through the REST API.
# List all webhook mappings
GET /api/webhook-mappings
# Create a new mapping
POST /api/webhook-mappings
# Update a mapping
PUT /api/webhook-mappings/:id
# Toggle active/inactive
PATCH /api/webhook-mappings/:id/toggleSee the API reference for full request/response schemas.
