Skip to content

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/:slug

The :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:

  1. Aether looks up the mapping by slug
  2. Verifies the auth (HMAC signature, bearer token, etc.)
  3. Extracts event + action → constructs a source_event string (e.g., issues:opened)
  4. Normalises to a canonical trigger (e.g., ticket.created) via the source_event_mappings table
  5. Resolves the tasks for that trigger
  6. 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

bash
curl -X POST http://localhost:8000/api/webhook-mappings/seed

Response:

json
{"created": 5, "skipped": 0}

The seed endpoint is idempotent — running it multiple times is safe.

YAML format

yaml
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

  1. Go to Settings → Webhooks in your repository (or organisation)
  2. Set the Payload URL to:
    https://your-aether-host/webhook/github-default
  3. Set Content type to application/json
  4. Choose Let me select individual events and enable at minimum:
    • Issues
    • Issue comments
    • Pull requests
  5. (Recommended) Set a Secret and configure HMAC auth below

Extraction rules

FieldSourcePath / Header
eventHeaderX-GitHub-Event
actionPayload$.action
issue_idPayload$.issue.number
ownerPayload$.repository.owner.login
repoPayload$.repository.name

YAML config with HMAC auth

yaml
- 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 eventActionCanonical trigger
issuesopenedticket.created
issueseditedticket.updated
issuesclosedticket.state_changed
pull_requestopenedvcs.request_opened
pull_requestsynchronizevcs.request_updated

Seed these mappings with:

bash
curl -X POST http://localhost:8000/api/tasks/sources/seed

Jira

Register the webhook in Jira

  1. Go to System → Advanced → WebHooks (Jira Admin)
  2. Create a new webhook with URL:
    https://your-aether-host/webhook/jira-default
  3. Select the events you want to receive (at minimum issue_created, issue_updated)

Extraction rules

FieldSourcePath / Header
eventHeaderX-Jira-Event (fallback: $.webhookEvent)
actionPayload$.issue_event_type_name
issue_idPayload$.issue.id
issue_keyPayload$.issue.key
project_idPayload$.issue.fields.project.id
project_keyPayload$.issue.fields.project.key

YAML config

yaml
- 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 instead

Jira 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 eventCanonical trigger
jira:issue_createdticket.created
jira:issue_updatedticket.updated
jira:issue_deletedticket.deleted

Plane

Register the webhook in Plane

  1. Go to Workspace Settings → Integrations → Webhooks
  2. Create a new webhook with URL:
    https://your-aether-host/webhook/plane-default
  3. Enable events: Issue created, Issue updated, Issue deleted, State changed

Extraction rules

FieldSourcePath / Header
eventHeaderX-Plane-Event (fallback: $.event)
actionPayload$.action
issue_idPayload$.data.id
project_idPayload$.data.project
workspacePayload$.data.workspace

YAML config

yaml
- 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 eventCanonical trigger
issue:createdticket.created
issue:updatedticket.updated
issue:deletedticket.deleted
issue:state:changedticket.state_changed

Auth Config Reference

TypeDescriptionRequired fields
noneNo verification — any request is accepted
hmacHMAC-SHA256 signature in a headersecret, header, algorithm
bearerAuthorization: Bearer <token> headertoken
basicHTTP Basic Authusername, password
headerCustom header with static valueheader_name, header_value

HMAC example

yaml
auth_config:
  type: "hmac"
  secret: "my-shared-secret"
  header: "X-Hub-Signature-256"
  algorithm: "sha256"      # sha256 or sha1

Bearer example

yaml
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:

bash
# 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/seed

All 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.

bash
# 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/toggle

See the API reference for full request/response schemas.

Released under the MIT License.