GitHub ↗

Webhooks

Momo has two distinct webhook types that are easy to confuse:

Type Where Purpose Payload
HTTP Alert Settings → Notifications → Channels Receive Momo notifications via HTTP momo.notification with title + body
Task Events Settings → Integrations → Task Events Automate on task changes task.created/updated/completed/deleted with task data

HTTP Alert (Notification Channel)

The HTTP Alert is a delivery channel for Momo notifications — alongside Web Push, ntfy, Pushover, Telegram, and Email. Whenever Momo sends a notification (e.g. Daily Quest, streak reminder, due tasks), it also POSTs it to your configured URL.

Setup: Settings → Notifications → Channels → + HTTP Alert

Payload

{
  "event": "momo.notification",
  "title": "Your Daily Quest is waiting",
  "body": "Today's mission: finish the React component",
  "url": "/dashboard",
  "tag": "daily-quest",
  "timestamp": "2026-04-25T08:00:00.000Z"
}
Field Type Description
event string Always "momo.notification"
title string Notification title
body string Notification body text
url string | null Relative app path to open
tag string | null Unique identifier for the notification type (e.g. daily-quest, streak, due-today)
timestamp string ISO 8601 timestamp

Common tag values

Tag Trigger
daily-quest Daily quest reminder
streak Streak reminder
due-today Tasks due today
overdue Overdue tasks
recurring-due Recurring tasks due
weekly-review Weekly review summary
morning-briefing Morning digest
achievement Achievement unlocked

Example: Home Assistant

# configuration.yaml
rest_command:
  momo_alert:
    url: "https://homeassistant.local/api/webhook/momo-alert"
    method: POST
    content_type: "application/json"

automation:
  - alias: "Momo Notification  Mobile"
    trigger:
      platform: webhook
      webhook_id: momo-alert
    action:
      service: notify.mobile_app
      data:
        title: ""
        message: ""

Example: n8n / Zapier

Add a Webhook Trigger node and use and as inputs for further actions.


Task Events (Outbound Webhooks)

Task Events are for automation. Momo sends structured JSON events to your configured endpoints whenever tasks change — independent of notification settings.

Setup: Settings → Integrations → Task Events → + Add Endpoint

You can configure up to 10 endpoints, each with its own signing secret and event filter.

Events

Event Trigger
task.created A task was created
task.updated A task was edited
task.completed A task was checked off
task.deleted A task was deleted

Payload

{
  "event": "task.completed",
  "timestamp": "2026-04-25T14:32:00.000Z",
  "task": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "title": "Finish the React component",
    "type": "ONE_TIME",
    "priority": "NORMAL",
    "topicId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "dueDate": "2026-04-25",
    "completedAt": "2026-04-25T14:32:00.000Z",
    "createdAt": "2026-04-20T09:00:00.000Z"
  }
}
Field Type Description
event string task.created / task.updated / task.completed / task.deleted
timestamp string ISO 8601 event timestamp
task.id string Task UUID
task.title string Task title
task.type string ONE_TIME, RECURRING, or DAILY_ELIGIBLE
task.priority string HIGH, NORMAL, or SOMEDAY
task.topicId string | null UUID of the associated topic
task.dueDate string | null Due date (YYYY-MM-DD)
task.completedAt string | null Completion timestamp (ISO 8601), only on task.completed
task.createdAt string Creation timestamp (ISO 8601)

Example: n8n Workflow

Webhook Trigger (POST /webhook/momo)
  → IF event == "task.completed"
    → HTTP Request → Notion API (update page)
    → Slack Message → #done

Example: Zapier

  1. Trigger: Webhooks by Zapier → Catch Hook
  2. Copy the Zapier URL into Momo as the endpoint URL
  3. Select the events you care about (or all of them)
  4. Action: e.g. add a row to Google Sheets, move a Trello card, etc.

Request Signing (HMAC-SHA256)

Both webhook types support optional request signing to verify the origin of incoming requests.

When a signing secret is configured, every request includes the header:

X-Momo-Signature: sha256=<hex-digest>

The digest is computed as HMAC-SHA256 over the raw JSON body using your secret as the key.

Verification examples

Node.js / TypeScript:

import { createHmac, timingSafeEqual } from "crypto";

function verifyMomoSignature(
  body: string,
  secret: string,
  signatureHeader: string
): boolean {
  const expected = `sha256=${createHmac("sha256", secret).update(body).digest("hex")}`;
  const received = Buffer.from(signatureHeader);
  const expectedBuf = Buffer.from(expected);
  if (received.length !== expectedBuf.length) return false;
  return timingSafeEqual(received, expectedBuf);
}

Python:

import hmac
import hashlib

def verify_momo_signature(body: bytes, secret: str, signature_header: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

PHP:

function verifyMomoSignature(string $body, string $secret, string $header): bool {
    $expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
    return hash_equals($expected, $header);
}

Important: Always use the raw request body before JSON-parsing for signature verification. Use timingSafeEqual / compare_digest / hash_equals — never a plain string comparison (timing attack protection).


At a Glance

Momo sends a notification
  │
  ├── Web Push (browser)
  ├── ntfy.sh
  ├── Pushover
  ├── Telegram
  ├── Email
  └── HTTP Alert  ← notification webhook
        Payload: { event: "momo.notification", title, body, ... }

A task is checked off
  │
  └── Task Events webhook  ← outbound webhook
        Payload: { event: "task.completed", task: { id, title, ... } }

See also