Docs / Ref. 01 aleja.app ↗

Incoming webhooks

An incoming webhook is a secret URL that posts messages into one text channel of a nest. Anything that can send an HTTP request can use it: CI pipelines, uptime monitors, cron jobs, home automation.

Security model

Nest channels are end-to-end encrypted: members encrypt on their own devices, and the server only ever stores ciphertext. A webhook can't work that way, because the sender is a plain HTTP client with no keys. So webhook posts are a deliberate, visible exception:

  • The server receives webhook text in plain form, encrypts it at rest with the server's model key (the same storage used for AI replies), and serves it to channel members.
  • Every webhook post carries a WEBHOOK badge in the client, which says the post is not end-to-end encrypted.
  • Don't send anything through a webhook that you wouldn't want the server operator to be able to read.

The server only stores a SHA-256 hash of each webhook token. The full URL is shown once, when the webhook is created or its URL is reset. If you lose it, reset it.

Creating a webhook

You need the Manage server permission (or Administrator, or nest ownership).

  1. Open Nest settings → Apps → Integrations.

  2. Enter a name, pick a text channel, and click New webhook.

  3. Copy the URL right away. It looks like:

    https://<api-host>/webhooks/<webhook-id>/<token>
    

From the same screen you can rename a webhook, move it to another text channel, reset its URL (the old URL stops working right away), or delete it. Messages a webhook already posted stay in the channel after it's deleted. Deleting the channel or the nest deletes its webhooks.

A nest can have up to 15 webhooks.

Posting a message

POST /webhooks/{id}/{token}
Content-Type: application/json
FieldTypeNotes
contentstringThe message text. Up to 2000 characters once composed.
textstringUsed when content is missing, so Slack-style senders work.
usernamestringOptional. Overrides the display name for this one post (1–80 characters). Invalid values fall back to the webhook's name.
embedsarrayOptional. Up to 10 objects with title, description, and url. Each embed becomes a block of lines under the content, so Discord-style senders still show something.

At least one of content, text, or an embed with text is required.

Example

curl -X POST "https://<api-host>/webhooks/<webhook-id>/<token>" \
  -H "Content-Type: application/json" \
  -d '{"content": "Deploy finished", "username": "CI"}'
{ "id": "6f3c…", "channel_id": "a91e…", "created_at": 1790812800000 }

GitHub Actions:

- name: Tell the nest
  run: |
    curl -fsS -X POST "$ALEJA_WEBHOOK" \
      -H "Content-Type: application/json" \
      -d "{\"content\": \"${{ github.repository }} deployed ${{ github.sha }}\"}"
  env:
    ALEJA_WEBHOOK: ${{ secrets.ALEJA_WEBHOOK }}

Responses

StatusMeaning
200Posted. The body has the new message's id, channel_id, and created_at.
400The body isn't valid JSON, the content is empty, or it's over 2000 characters.
415The Content-Type header isn't application/json.
422The JSON has the wrong shape, for example content is a number.
404Unknown webhook, wrong token, or the webhook's channel no longer exists. A wrong token and a missing webhook look the same on purpose.
413The request body is over 32 KB.
429Rate limited. Each webhook gets a burst of 5 messages, refilling over 3 seconds. The Retry-After header says how long to wait.
403The calling network is IP-banned by a server admin.

Errors return { "error": "<message>" }.

How posts appear

  • The author is the webhook's name, or the username override, with a generated avatar and a WEBHOOK badge.
  • Webhook posts can't be edited. Members with Manage messages can delete them. Everyone can react to them, reply to them, and pin them where pinning is allowed.
  • Posts aren't tied to a member's profile, so there's no profile card, mention, block, or report action on the author.

Management API

All of these need a session token (Authorization: Bearer <token>) for a member with Manage server.

Method and pathBodyResult
GET /v1/servers/{server_id}/webhooks{ webhooks: [Webhook] }
POST /v1/servers/{server_id}/webhooks{ name, channel_id }Webhook plus token and path (shown once)
PATCH /v1/webhooks/{id}{ name?, channel_id? }Webhook
POST /v1/webhooks/{id}/token{ id, token, path }. The old token stops working.
DELETE /v1/webhooks/{id}{ ok: true }

A Webhook is { id, server_id, channel_id, channel_name, name, created_by, created_by_name, created_at, last_used_at }. channel_name is null if the channel was removed. Creating, deleting, and resetting webhooks is recorded in the server's audit log.