MDH API Documentation

Build on the delivery engine.

Everything you need to publish events, register endpoints, verify signatures, and replay deliveries — plus downloadable specs and an AI assistant that knows this API end to end.

Introduction

MDH is a webhook delivery and monitoring platform. Send events in and fan them out to customer endpoints with signed, retried, observable delivery — or point MDH at your own systems and watch them live: inbound webhooks, REST uptime checks, machine agents, and Node.js middleware all stream into one command center.

Five building blocks: an App (a workspace with its own keys and live stream), an Event (something that happened), an Endpoint (a URL subscribed to event patterns), a Delivery (one signed attempt, fully logged), and a Monitor (a live signal source — uptime poller, machine agent, or Node middleware).

Base URL
https://mdhapi.com/api

Quickstart

Create an app in the dashboard first — every app gets its own keys, URLs, and live stream. Then three ways to get data flowing in under a minute:

1 · Publish an event (API key)
curl -X POST https://mdhapi.com/api/ingest/APP_ID/events \
  -H "Content-Type: application/json" \
  -H "X-API-Key: mdk_live_..." \
  -d '{"type":"payment.succeeded","data":{"amount":4200}}'
2 · Receive a webhook (public URL per app)
curl -X POST https://mdhapi.com/api/hooks/YOUR_HOOK_SLUG \
  -H "Content-Type: application/json" \
  -d '{"type":"invoice.paid","amount":12900}'
3 · Watch a machine (30-second agent)
pip install psutil requests
curl -o mdh_agent.py https://mdhapi.com/api/agent/script
python mdh_agent.py --api https://mdhapi.com --key mdk_live_...

Everything you send lands in the app's live event stream in real time.

Authentication

Three credential types, each built for a different caller. Every endpoint in the docs marks which ones it accepts: *(public)*, *(key)*, or *(session or PAT)*.

Personal access token (PAT) — for scripts, CI, and anything acting as *you* across the whole API (/apps, /auth/me, config, publishing). Create one in Settings → API; it is shown exactly once. Scopes: read (GET only), write (no deletes), admin (everything). A PAT can never create or revoke other tokens.

Authorization header
Authorization: Bearer mdh_pat_...

App API key — for event intake and agents, scoped to a single app. Created in the app's dashboard → Keys, starts with mdk_live_, shown once, rotated or revoked instantly. Scopes: read, publish, admin.

Headers
X-API-Key: mdk_live_...     (event intake)
X-Agent-Key: mdk_live_...   (machine + node agents)

Session cookie — the browser dashboard only. Log in with email + password, Google, or GitHub; an httpOnly cookie rides every request. Scripts should always use a PAT instead — no login flow, no cookies.

WebSocket streams
wss://mdhapi.com/api/ws/apps/APP_ID?token=mdh_pat_...
(browsers just use the session cookie)

Webhook Receiver

Every Webhook Receiver app gets a public, unguessable intake URL. Point GitHub, Stripe, or any producer at it; MDH records each request and mirrors it into the live stream instantly.

POST/hooks/{slug}
Public intake. Any JSON body up to 64 KB. The event type is read from an X-GitHub-Event-style header or a type/event field in the payload. Store the producer's signing secret (e.g. a Stripe whsec_…) in the app config and forged requests are rejected with 400.
GET/apps/{app_id}/hooks
List the 100 most recent inbound requests — bodies, content types, forward status. (session or PAT)
POST/apps/{app_id}/hooks/{hook_id}/replay
Replay a request, re-forwarding to your routing targets. (session or PAT)

Routing lives in the app config: set forward_url for a single pass-through destination, or forward_targets — a list of destinations, each with an optional transform template — to fan one inbound webhook out to several services in the exact shape each expects. The producer's original signature header is always passed through.

Transform template (per target)
{
  "url": "https://api.acme.dev/alerts",
  "template": {
    "text": "Order {{data.order_id}} paid {{data.amount}}",
    "raw": "{{$raw}}"
  }
}

{{dot.path}} pulls values from the incoming JSON; {{$raw}} embeds the untouched body.

Events API

POST/ingest/{app_id}/events
Publish an event. Auth: app API key (X-API-Key or Bearer, publish scope). Body: { type, data }. Returns the event id and mirrors it into the live stream instantly.
GET/apps/{app_id}/events
Recent events, newest first (limit up to 200). (session or PAT)
POST/apps/{app_id}/events/test
Fire a test event straight from the dev console. (session or PAT)
POST/apps/{app_id}/event-types
Declare an event type like payment.succeeded with a description. (session or PAT)
DELETE/apps/{app_id}/event-types/{type}
Remove a declared type. (session or PAT — needs admin scope)

Endpoints API

Outbound delivery — MDH POSTs your events to customer URLs, signed and retried. Endpoints live under each app.

POST/apps/{app_id}/endpoints
Register a URL with event patterns. Empty events matches everything; payment.* matches a namespace. The HMAC signing secret is returned once — store it. (session or PAT)
GET/apps/{app_id}/endpoints
List endpoints with health: attempts and success rate. (session or PAT)
DELETE/apps/{app_id}/endpoints/{ep_id}
Remove an endpoint. (session or PAT — needs admin scope)
POST/apps/{app_id}/publish
Publish { type, data } to every matching endpoint. (session or PAT — needs write scope)
GET/apps/{app_id}/deliveries
Every attempt — status, latency, retries. (session or PAT)

Monitors & Agents

Live signal sources, all streaming into the same dashboard. Any of them can text you on failures — enable SMS in Settings → Profile.

REST API monitor — MDH pings your URL on your interval and charts uptime and latency. Down/recovered transitions alert you.

PATCH/apps/{app_id}/config
Set target_url, interval_s (min 10), and optional auth_header/auth_value. First ping lands within seconds. (session or PAT)
GET/apps/{app_id}/uptime
Recent checks plus uptime %, average and P99 latency. (session or PAT)

Cron monitor — jobs ping a heartbeat URL when they finish; silence past the expected interval raises cron.missed. SSL monitor — server-side certificate checks with ssl.expiring alerts inside 14 days. Docker agent — container starts, stops, and health. Log watcher — tails any file, ships matching lines. SSH agent — login and command activity.

Machine agent — a dependency-light Python agent streams CPU, memory, disk, and processes from any Mac, Linux, or Windows host.

GET/agent/script
mdh_agent.py (machine). Also public: /agent/ssh-script, /agent/docker-script, /agent/log-script, /agent/listen-script.
POST/agent/register
First call; returns a host_id. Auth: X-Agent-Key.
POST/agent/metrics
Metrics loop: { host_id, system: {cpu, mem_pct, disk_pct}, children: [...] }. Auth: X-Agent-Key.

The stream shows agent.connected, agent.disconnected (after 90s of silence), and resource.pressure events automatically.

Node.js middleware — drop mdh-monitor.mjs (from your app's dashboard) into any Express app. It reports per-route rpm, error rate, and p95 every 15 seconds — always, even with zero traffic, so the dashboard knows your app is alive.

POST/agent/node-metrics
{ routes: [{ method, route, rpm, err_pct, p95 }], heap_mb }. Auth: X-Agent-Key.

The stream shows agent.connected on first report and route.discovered when a new endpoint appears.

Signatures

Every delivery carries X-MDH-Signature: a timestamp and an HMAC-SHA256 of {t}.{raw body} using the endpoint secret. Always verify before processing, and reject timestamps older than 5 minutes.

Verify in Node.js
const crypto = require("crypto");

function verifyMdh(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(",").map((kv) => kv.split("="))
  );
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Retries & Replay

Failed outbound deliveries retry with backoff — 1s, 5s, and 30s after the first attempt, four attempts total — before the delivery is marked failed. Every attempt is logged with status and latency in the deliveries list.

Inbound webhooks are never retried by MDH (the producer owns retries), but any received request can be replayed from the dashboard or the API, with optional re-forwarding to your routing target.

POST/apps/{app_id}/hooks/{hook_id}/replay
Replay an inbound request. (session)

Live Stream

Every app has a realtime channel — the dashboard's live stream, monitor panels, and connection badges all ride it. Browsers authenticate with the session cookie automatically; scripts use a personal access token.

WS/ws/apps/{app_id}
Streams event, hook, uptime, metrics, agents, nodestats, and delivery messages as they happen, plus a ping heartbeat every 20 seconds. Auth: session cookie, Authorization: Bearer mdh_pat_… header, or ?token= query param.
Watch from a terminal — Stream CLI (recommended)
curl -o mdh_listen.mjs https://mdhapi.com/api/agent/listen-script
MDH_TOKEN=mdh_pat_... node mdh_listen.mjs APP_ID
…or raw wscat
npx wscat -c "wss://mdhapi.com/api/ws/apps/APP_ID?token=mdh_pat_..."

Rate Limits

No per-key request caps are enforced today. Plans carry a monthly event allowance (Developer: 10,000 events/month) and your current usage meter lives on the dashboard overview. Delivery retries and monitor intervals are tuned per app, so a slow endpoint never affects the rest of your traffic.

Downloads

AI Assistant

Ask questions about this API or describe an integration and get generated SDK code. Pick your model per question.

demo preview

How do

thinking…

This is a looping preview. The real assistant answers from live docs, the changelog, and your own app's data.