Skip to content

Webhook Triggers

Any task can be triggered via an HTTP POST to a unique webhook URL. This lets external systems — GitHub Actions, monitoring tools, other APIs — fire a task on demand without needing an ApiMeld account.

Setting up a webhook trigger

  1. Open a task — the Webhook Trigger section is in the right column of the edit form
  2. Toggle Enable Webhook on
  3. Optionally enter a Shared Secret (recommended — see below)
  4. Save the task — the webhook URL is then displayed with a copy button:
    https://tasks.example.com/api/webhook/a1b2c3d4-e5f6-7890-abcd-ef1234567890

The GUID in the URL is unique to this task. Anyone with this URL can trigger the task, so treat it like a secret.

Triggering a task

Send an HTTP POST to the webhook URL. No authentication header is required — the GUID in the URL serves as the token.

bash
curl -X POST https://tasks.example.com/api/webhook/a1b2c3d4-e5f6-7890-abcd-ef1234567890

With a payload:

bash
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"event": "deploy", "version": "1.2.3", "environment": "production"}' \
  https://tasks.example.com/api/webhook/a1b2c3d4-e5f6-7890-abcd-ef1234567890

The response is immediate — ApiMeld queues the task and returns 202 Accepted. The task runs asynchronously.

Accessing the payload in scripts

When a webhook fires a task, ApiMeld injects a $webhook (PowerShell) or webhook (JavaScript/TypeScript/Python) object containing the request details.

PropertyDescription
Body / bodyRaw request body string (JSON, form data, etc.)
Params / paramsQuery string parameters as a key/value object
Headers / headersRequest headers as a key/value object
TriggeredAt / triggeredAtISO 8601 timestamp of when the webhook was received

PowerShell:

powershell
if ($webhook.Body) {
    $data = $webhook.Body | ConvertFrom-Json
    $logger.Info("Deploy version: $($data.version) to $($data.environment)")
}

JavaScript:

javascript
if (webhook.body) {
  const data = JSON.parse(webhook.body)
  logger.info(`Deploy version: ${data.version} to ${data.environment}`)
}

TypeScript:

typescript
if (webhook.body) {
  const data = JSON.parse(webhook.body) as { version: string; environment: string }
  logger.info(`Deploy version: ${data.version} to ${data.environment}`)
}

Python:

python
import json

if webhook_body:
    data = json.loads(webhook_body)
    print(f"Deploy version: {data['version']} to {data['environment']}")

Bash:

bash
if [ -n "$WEBHOOK_BODY" ]; then
  version=$(echo "$WEBHOOK_BODY" | jq -r '.version')
  echo "Deploy version: $version"
fi

C#:

csharp
if (!string.IsNullOrEmpty(WebhookBody))
{
    var data = JsonSerializer.Deserialize<JsonElement>(WebhookBody);
    Logger.Info($"Deploy version: {data.GetProperty("version").GetString()}");
}

Python variable names

VariableContent
webhook_bodyRequest body string
webhook_paramsQuery params dict
webhook_headersRequest headers dict
webhook_triggered_atTimestamp string

Bash variable names

VariableContent
$WEBHOOK_BODYRequest body string
$WEBHOOK_TRIGGERED_ATTimestamp string

Use cases

  • GitHub Actions: trigger a deployment task after a successful build
  • Monitoring: trigger an alert-response task when Grafana fires an alert
  • External schedulers: trigger ApiMeld tasks from an existing scheduler you're migrating away from
  • IoT / edge devices: trigger a data ingestion task when a device reports in

Shared Secret

A shared secret adds a second layer of protection on top of the GUID URL. When set, every request must include the secret in the X-Webhook-Secret header — requests without it or with the wrong value are rejected with 404 (not 401, to avoid confirming that the GUID is valid).

Set the shared secret when creating or editing the webhook trigger. Store it alongside the webhook URL in your calling system.

bash
# Trigger with a shared secret
curl -X POST \
  -H "X-Webhook-Secret: your-shared-secret" \
  https://tasks.example.com/api/webhook/a1b2c3d4-e5f6-7890-abcd-ef1234567890

With a payload:

bash
curl -X POST \
  -H "X-Webhook-Secret: your-shared-secret" \
  -H "Content-Type: application/json" \
  -d '{"event": "deploy", "version": "1.2.3"}' \
  https://tasks.example.com/api/webhook/a1b2c3d4-e5f6-7890-abcd-ef1234567890

GitHub Actions example:

yaml
- name: Trigger ApiMeld task
  run: |
    curl -X POST \
      -H "X-Webhook-Secret: ${{ secrets.APIMELD_WEBHOOK_SECRET }}" \
      -H "Content-Type: application/json" \
      -d "{\"ref\": \"${{ github.ref }}\", \"sha\": \"${{ github.sha }}\"}" \
      https://tasks.example.com/api/webhook/a1b2c3d4-e5f6-7890-abcd-ef1234567890

TIP

Use a long random string as the shared secret — at least 32 characters. You can generate one with openssl rand -hex 32.

Rotating the webhook URL

If your webhook URL is compromised, disable and re-enable the webhook trigger on the task — a new GUID is generated and the old URL stops working.

Combining with a cron schedule

A task can have both a cron schedule and a webhook trigger active simultaneously. The task runs on its schedule and can also be fired on demand via the webhook.