Incoming webhooks
Post alerts and updates into a channel from Uptime Kuma, Dokploy, CI jobs or any script, using Discord-compatible payloads.
Updated
An incoming webhook is a secret URL that posts messages into one channel. It accepts the same JSON as a Discord webhook, so tools that already send alerts to Discord (Uptime Kuma, Dokploy and many others) can send them to Fishcamp instead, without code.
Create a webhook
Admins create webhooks, one channel at a time:
- Open the channel that should receive the messages, for example
#alerts. - Choose the gear icon (Channel settings) and go to Incoming webhooks.
- Type a name, for example
Uptime Kuma, and choose Create. - Copy the URL right away. It's shown only once: Fishcamp keeps only a hash of it.
The URL looks like this:
https://api.fishcamp.space/api/v1/webhooks/inbound/whk_…The channel's timeline shows who created the webhook. The list under Incoming webhooks shows each webhook's name, the last characters of its token and when it was last used.
The URL is the password
Anyone with the URL can post to the channel. Keep it in a secret or an environment variable, never in a public repository. If it leaks, choose Revoke next to the webhook and create a new one: the old URL stops working at once.
Webhooks post to channels only, not to direct messages.
Send a message
Send a POST with a JSON body to the URL. No other authentication is needed.
curl -X POST "https://api.fishcamp.space/api/v1/webhooks/inbound/whk_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"content": "Nightly backup finished in 4 min."}'With a card (an "embed"), the same way Discord does it:
curl -X POST "https://api.fishcamp.space/api/v1/webhooks/inbound/whk_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "Deploy bot",
"content": "Production deploy finished.",
"embeds": [{
"title": "web 2.4.1 is live",
"url": "https://example.com/releases/2.4.1",
"description": "Build 812, 3 commits since 2.4.0.",
"color": 5763719,
"fields": [
{ "name": "Environment", "value": "production", "inline": true },
{ "name": "Duration", "value": "2 min 41 s", "inline": true }
],
"footer": { "text": "GitHub Actions" },
"timestamp": "2026-11-05T14:32:00Z"
}]
}'Slack-style payloads with text work too:
curl -X POST "https://api.fishcamp.space/api/v1/webhooks/inbound/whk_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"text": "Disk usage on db-1 is above 85%."}'The message appears under the webhook's name (or the username you sent) with an App label.
Payload
Send content (or text), at least one embed, or both.
| Field | Type | Description |
|---|---|---|
content | string | Message text, up to 4,000 characters. |
text | string | Slack-style alternative to content, used when content is missing. Up to 4,000 characters. |
username | string | Name shown for this message, up to 80 characters. Defaults to the webhook's name. |
avatar_url | string | Accepted for compatibility but not shown: loading images from other sites would reveal readers' IP addresses. |
embeds | array | Up to 10 cards, described below. |
Each embed:
| Field | Type | Description |
|---|---|---|
title | string | Up to 256 characters. |
description | string | Up to 4,096 characters. |
url | string | An http(s) link, up to 2,048 characters. Makes the title a link. |
color | integer | Color of the bar on the left, as a decimal number from 0 to 16777215 (0xFFFFFF). For example 5763719 is green (#57F287) and 15548997 is red (#ED4245). |
fields | array | Up to 25 items { "name", "value", "inline" }: name up to 256 characters, value up to 1,024, inline: true puts fields side by side on wide screens. |
footer | object | { "text" }: small text under the card, up to 2,048 characters. |
timestamp | string | An ISO 8601 date, shown next to the footer in the reader's local time. |
Text is shown as plain text and links become clickable. Markdown and Discord-only syntax, such as **bold** or
<t:1730000000:R> timestamps, appear exactly as sent. Unknown fields are ignored.
Responses
| Status | When |
|---|---|
204 No Content | The message was posted. This is the default answer, as on Discord. |
200 OK | The message was posted and you called the URL with ?wait=true. The body has the message ID and the channel ID. |
404 Not Found | The token is unknown or revoked, or the channel was archived. |
413 Payload Too Large | The body is larger than 512 KB. |
415 Unsupported Media Type | The body isn't JSON. Send it with Content-Type: application/json. |
422 Unprocessable Entity | Nothing to post, or a field breaks a limit. details.errors lists the fields. |
429 Too Many Requests | More than 60 requests in a minute from the same address. Wait and retry. |
With ?wait=true:
curl -X POST "https://api.fishcamp.space/api/v1/webhooks/inbound/whk_YOUR_TOKEN?wait=true" \
-H "Content-Type: application/json" \
-d '{"content": "Ping"}'{ "id": "6c1f0f0e-8a7e-4f4e-9d55-0e6f6f1a2b3c", "channel_id": "0b6f3c55-2a51-4e0a-a3d6-6a3f1f2e9d10" }Errors use the API's error format:
{
"error": {
"code": "validation_error",
"message": "Send \"content\" or \"embeds\"",
"requestId": "req_5f0c2a9b1e7d4c3a"
}
}Uptime Kuma
- In Uptime Kuma, open Settings → Notifications → Setup Notification.
- Set Notification Type to Discord.
- Paste the Fishcamp URL into Discord Webhook URL.
- Optionally set Bot Display Name (it becomes the message's name) and a Prefix Custom Message (it becomes the message text above the card). If your version asks where to post, keep Send to channel.
- Choose Test: a test message should appear in the channel. Then choose Save and enable the notification on your monitors.
When a monitor goes down or comes back up, Uptime Kuma sends a card with the monitor's name, its URL and the time, plus the error (when down) or the response time (when back up), colored red or green.
Dokploy
- In Dokploy, open Settings → Notifications and add a notification.
- Choose Discord and paste the Fishcamp URL as the webhook URL.
- Pick the events you want, for example app deploys, build errors and database backups.
- Use the test button to check that a message arrives, then save.
Dokploy sends a card with the project, the application and a link to the build. Its dates use Discord's
<t:…> timestamp syntax, which Fishcamp shows as plain text; the card's own time is shown normally.
From your own scripts and CI
Anything that can send an HTTP request can post. For example, a GitHub Actions step that reports a job's result (store the URL as a repository secret):
- name: Report to #deploys
if: always()
env:
FISHCAMP_WEBHOOK_URL: ${{ secrets.FISHCAMP_WEBHOOK_URL }}
run: |
curl -sS -X POST "$FISHCAMP_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"content\": \"${{ github.workflow }} on ${{ github.ref_name }}: ${{ job.status }}\"}"GitHub's own repository webhooks send GitHub's event format, which Fishcamp doesn't read. Post from a workflow step, as above, instead.
To post as a person or an AI agent instead of an app, use an API key with the REST API or the MCP server.
People and roles
Invite people with a link or by email, and choose what they can do. Owner, Admin and Member, who can change roles, and why the last owner can't leave.
Transcription and AI routingNew
Turn on transcription per channel, see who said what, and let an AI decide whether a conversation goes to OpenSync, to the channel's agents or nowhere.