Incoming webhooks

An incoming webhook is a secret URL that posts into one channel. It speaks Discord's webhook format, so anything that can "notify Discord" works without changes: CI servers, uptime monitors, server panels, n8n, Zapier, Make and your own scripts. The same URL also takes Slack's format, and GitHub can send its events to it.

Make one

  1. In Missived, open Org Settings → Integrations → Incoming webhooks → Add incoming webhook. You need the Manage integrations permission (Owners, Admins, or a custom role that grants it).
  2. Give it a name (shown on its messages with an APP badge), pick the channel it posts into, and optionally an avatar (an https PNG, JPEG, GIF or WebP).
  3. Copy the URL. It is shown once: Missived keeps only a hash of it. If you lose it or it leaks, choose Regenerate URL; the old URL stops working at once.

You can also make and manage webhooks over the API.

The three URLs

URL Speaks Use it for
…/hooks/<id>/<token> Discord's execute-webhook JSON Anything with a "Discord webhook" option, curl, scripts
…/hooks/<id>/<token>/slack Slack's incoming-webhook JSON Tools that only know Slack
…/hooks/<id>/<token>/github GitHub webhook events A repository's or organisation's webhook settings

Discord format

curl -H "Content-Type: application/json" \
  -d '{"content": "Deploy finished", "username": "Deploys",
       "embeds": [{"title": "api v1.4.2", "description": "All checks passed", "color": 3066993}]}' \
  'https://api.missived.com/hooks/<id>/<token>'

Errors use Discord's shapes, such as {"code": 50035, "message": "Invalid Form Body", "errors": …}, 10015 Unknown Webhook and 50027 Invalid Webhook Token, so tools report them sensibly.

Slack format

Post Slack's JSON to the /slack URL:

It answers ok, as Slack does.

GitHub

In the repository, open Settings → Webhooks → Add webhook:

Missived checks GitHub's signature on every delivery and refuses unsigned or wrongly signed ones. A redelivery of the same body within 24 hours is answered duplicate and not posted twice.

These events become cards: ping (the "it's connected" card), pushes (up to five commits), branch and tag creation and deletion, pull requests opened, closed, merged, reopened or marked ready for review, issues opened, closed or reopened, issue and pull request comments, published releases, and finished workflow runs. Other events are answered 204 and ignored.

Examples

Uptime Kuma: Settings → Notifications → Setup Notification → type Discord → paste the URL as the Discord Webhook URL → Test.

GitHub Actions: save the URL as a repository secret named MISSIVED_WEBHOOK, then add a step:

      - name: Tell Missived
        if: always()
        env:
          MISSIVED_WEBHOOK: ${{ secrets.MISSIVED_WEBHOOK }}
          TITLE: "${{ github.workflow }}: ${{ job.status }}"
          RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
        run: |
          jq -n --arg t "$TITLE" --arg u "$RUN_URL" '{embeds: [{title: $t, url: $u}]}' |
            curl -sS -H "Content-Type: application/json" -d @- "$MISSIVED_WEBHOOK"

n8n, Zapier, Make: use their Discord node or action and paste the URL.

Limits

Messages from a webhook are stored like any other message: searchable, encrypted at rest where the org encrypts content, and they notify the people they mention.

Manage webhooks over the API

The same management the Integrations page does is available to a token with the webhooks:write scope:

Operation Does
GET /v1/orgs/{orgId}/incoming-webhooks List webhooks, with the plan's cap
POST /v1/orgs/{orgId}/incoming-webhooks Make one: { "name", "channelId", "avatarUrl" }
PATCH /v1/orgs/{orgId}/incoming-webhooks/{id} Rename, move, change the avatar, or pause / resume ("paused": true)
POST /v1/orgs/{orgId}/incoming-webhooks/{id}/rotate Issue a new URL; the old one stops working
POST /v1/orgs/{orgId}/incoming-webhooks/{id}/github-secret Issue a new GitHub secret
GET /v1/orgs/{orgId}/incoming-webhooks/{id}/deliveries Recent posts and refusals (statuses only, never content)
DELETE /v1/orgs/{orgId}/incoming-webhooks/{id} Delete it; its old messages stay, with their APP badge
curl -X POST https://api.missived.com/v1/orgs/<orgId>/incoming-webhooks \
  -H "Authorization: Bearer $MISSIVED_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Deploys", "channelId": "<channelId>"}'

The answer (201) holds the webhook, its url and path, and githubSecret. The URL and the GitHub secret are returned only here and by rotate and github-secret; store them right away.

Who may do what:

These routes are older than the rest of /v1: some field names are camelCase (channelId and avatarUrl in requests, githubSecret in answers), lists answer { "webhooks": [...], "cap" } rather than data, and errors answer { "error": "A sentence.", "code": "…" } rather than the usual { "error": { "code", "message" } }. Every change appears in the org's Audit log.