addrbookFor publishers
← The evidence ledger

Evidence row 221267

Observed , by probe.mcp/1.

Kind
mcp.tools
About
endpoint 9e97907e-8c2c-4962-836f-58914a77ab83
Recorded
Previous row
None: the first of its kind about this subject
Body SHA-256
541eef29417c9ec07dc90c700747211d882408a5a1106b860cb81c3a8d72c233
Leaf
0x01ef523672bc2e31dbb95f8a526438bc7b16ac239dbf0da3502cf5bbf715f69d

What was observed

{
  "count": 21,
  "tools": [
    {
      "description": "Archive a template (POST /templates/{id}/archive; write scope). Sends that reference it fail until it is restored with unarchive_template; its name stays reserved meanwhile. Confirm with the user first if it may be in production use.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "templateId": {
            "description": "The template id (UUID).",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "required": [
          "templateId"
        ],
        "type": "object"
      },
      "name": "archive_template"
    },
    {
      "description": "Create a template (POST /templates; write scope). `channelKind` is permanent and must match the channels you'll send through (list_channels `templateKind`). It is saved as an unpublished draft unless `publish: true`; sending by templateId needs a published version (publish_template). Names are unique per workspace including archived templates: a collision returns 409, so unarchive_template the old one instead. Returns `{ id, requiredParams }`. Designed (drag-and-drop) email templates can only be made in the dashboard.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "channelKind": {
            "description": "Which channel family the template renders for. Must match the `templateKind` of the channels you will send through (list_channels). Can't be changed after creation.",
            "enum": [
              "email_html",
              "fcm_basic",
              "slack_text",
              "discord_text",
              "telegram_text",
              "sms_text",
              "webhook_json"
            ],
            "type": "string"
          },
          "content": {
            "additionalProperties": {},
            "description": "Channel-shaped template body; the shape is fixed by `channelKind`: email_html → `{ subject, bodyHtml, bodyText }`; fcm_basic → `{ title, body, imageUrl?, data?: { [key]: string } }`; slack_text → `{ text }`; discord_text → `{ content }` (max 2000 chars); telegram_text → `{ text, disableLinkPreview? }`; sms_text → `{ text }` (plain text, max 1600; long text is split into several billed SMS — on sendsms_mn into parts of 159 plain-Latin / 69 Cyrillic chars); webhook_json → `{ title, body, data?: { [key]: string } }`. Use `{{name}}` placeholders and `{{#items}}…{{/items}}` sections; the required params are derived from them automatically.",
            "propertyNames": {
              "type": "string"
            },
            "type": "object"
          },
          "folder": {
            "description": "Optional folder path for organisation, e.g. \"billing\".",
            "maxLength": 200,
            "type": "string"
          },
          "logDescription": {
            "description": "One-line description for the in-app feed and delivery logs. Placeholders allowed.",
            "maxLength": 2000,
            "minLength": 1,
            "type": "string"
          },
          "logTitle": {
            "description": "Short title shown in the in-app feed and delivery logs, e.g. \"Invoice {{number}} is due\". Placeholders allowed.",
            "maxLength": 500,
            "minLength": 1,
            "type": "string"
          },
          "name": {
            "description": "Template name, unique per workspace (archived templates included).",
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "paramOverrides": {
            "additionalProperties": {
              "properties": {
                "description": {
                  "maxLength": 500,
                  "type": "string"
                },
                "optional": {
                  "type": "boolean"
                }
              },
              "type": "object"
            },
            "description": "Per-param metadata keyed by param name: `{ optional?: boolean, description?: string }`. Mark a placeholder optional so sends may omit it.",
            "propertyNames": {
              "type": "string"
            },
            "type": "object"
          },
          "publish": {
            "description": "true = publish this version immediately so sends by templateId use it. Default false (saved as draft).",
            "type": "boolean"
          }
        },
        "required": [
          "name",
          "channelKind",
          "content",
          "logTitle",
          "logDescription"
        ],
        "type": "object"
      },
      "name": "create_template"
    },
    {
      "description": "Create an outbound webhook that Ray POSTs signed event payloads to (POST /tenant-webhooks; write scope; Pro plan or higher, otherwise 403). The URL must be public HTTPS. The response contains the signing `secret` exactly once: show it to the user to store (e.g. as an env var), since it can't be read again, only rotated with update_webhook. Read the `webhooks` docs page for the signature scheme.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "events": {
            "description": "Events to receive: \"notification.delivered\", \"notification.failed_terminal\" (one per recipient), and \"send.completed\" (once per send, with status totals).",
            "items": {
              "enum": [
                "notification.delivered",
                "notification.failed_terminal",
                "send.completed"
              ],
              "type": "string"
            },
            "minItems": 1,
            "type": "array"
          },
          "name": {
            "description": "Label for the webhook, e.g. \"Production delivery events\".",
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "url": {
            "description": "Public HTTPS endpoint that will receive the events.",
            "type": "string"
          }
        },
        "required": [
          "name",
          "url",
          "events"
        ],
        "type": "object"
      },
      "name": "create_webhook"
    },
    {
      "description": "Delete (archive) a webhook so it receives no more events (DELETE /tenant-webhooks/{id}; write scope). It cannot be restored through the API; confirm with the user first.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "webhookId": {
            "description": "The webhook id (UUID).",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "required": [
          "webhookId"
        ],
        "type": "object"
      },
      "name": "delete_webhook"
    },
    {
      "description": "Email link click counts grouped by destination URL (GET /clicks) for a `sendId`, a `campaignId`, or both (at least one is required). Only links from email sends made with `trackClicks: true` are counted.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "campaignId": {
            "description": "The `campaignId` passed to send_notification, to total clicks across many sends.",
            "maxLength": 256,
            "minLength": 1,
            "type": "string"
          },
          "sendId": {
            "description": "A sendId from send_notification.",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "type": "object"
      },
      "name": "get_click_stats"
    },
    {
      "description": "Delivery status of a send (GET /sends/{id}): `aggregate` counts per status over the whole send (pending, claimed, delivered, failed_retryable, failed_terminal, suppressed) plus one page of per-recipient delivery rows including provider errors. Right after sending, rows are usually `pending`; check again after a few seconds. Feed-only sends have no rows. For more rows, pass the returned `nextCursor` as `cursor`.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "cursor": {
            "description": "`nextCursor` from the previous page.",
            "minLength": 1,
            "type": "string"
          },
          "limit": {
            "description": "Rows per page (default 50, max 200).",
            "maximum": 200,
            "minimum": 1,
            "type": "integer"
          },
          "sendId": {
            "description": "The `sendId` returned by send_notification or test_send_template.",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "required": [
          "sendId"
        ],
        "type": "object"
      },
      "name": "get_send_status"
    },
    {
      "description": "Get one template with its published version and its current draft (GET /templates/{id}): content, logTitle/logDescription, `requiredParams` a send must supply, and paramOverrides. Read this before update_template_draft, which needs the full body.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "templateId": {
            "description": "The template id (UUID).",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "required": [
          "templateId"
        ],
        "type": "object"
      },
      "name": "get_template"
    },
    {
      "description": "The workspace's plan, subscription status and this calendar month's usage against the monthly quota (GET /billing/subscription). `used`/`remaining` are null on unmetered plans. On a hard-capped plan, sends beyond the quota fail with HTTP 402 quota_exceeded.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {},
        "type": "object"
      },
      "name": "get_usage"
    },
    {
      "description": "Get one outbound webhook (GET /tenant-webhooks/{id}) including its delivery health (consecutiveFailures, lastDeliveryAt, lastFailureAt, lastError).",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "webhookId": {
            "description": "The webhook id (UUID).",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "required": [
          "webhookId"
        ],
        "type": "object"
      },
      "name": "get_webhook"
    },
    {
      "description": "List the delivery channels configured in this workspace (GET /channels). Call this FIRST before sending: each entry's `id` is the `channelConfigId` for send_notification and test_send_template, `kind` is the channel type (ses_email, smtp_email, fcm_push, slack_webhook, discord_webhook, telegram_bot, twilio_sms, sendsms_mn, generic_webhook), `templateKind` is the template channelKind it accepts, and `recipientSchema` is the exact JSON Schema of the `recipient` it expects. Channels and their provider credentials are set up in the Ray dashboard, not through this API; if the list is empty, ask the user to add one there.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {},
        "type": "object"
      },
      "name": "list_channels"
    },
    {
      "description": "Read one end user's in-app notification feed (GET /notifications): the entries created by sends with `feed` or `showInFeed`. This is what an in-app inbox would show, not delivery status (use get_send_status for that). Paginate by passing `nextCursor` back as `cursor`.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "after": {
            "description": "Only entries created after this ISO 8601 UTC datetime (e.g. \"2026-09-01T00:00:00Z\").",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
            "type": "string"
          },
          "before": {
            "description": "Only entries created before this ISO 8601 UTC datetime.",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
            "type": "string"
          },
          "channelConfigId": {
            "description": "Only entries whose first delivery used this channel.",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          },
          "cursor": {
            "description": "`nextCursor` from the previous page.",
            "minLength": 1,
            "type": "string"
          },
          "externalUserId": {
            "description": "The end user whose feed to read (the `externalUserId` used when sending).",
            "maxLength": 256,
            "minLength": 1,
            "type": "string"
          },
          "limit": {
            "description": "Entries per page (default 50, max 200).",
            "maximum": 200,
            "minimum": 1,
            "type": "integer"
          },
          "templateId": {
            "description": "Only entries from this template.",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "required": [
          "externalUserId"
        ],
        "type": "object"
      },
      "name": "list_feed_notifications"
    },
    {
      "description": "List message templates (GET /templates): id, name, folder, channelKind, and `publishedVersionId` (null = never published, so it cannot be sent by templateId yet). Archived templates are hidden unless `includeArchived` is true. Use get_template for content and required params.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "folder": {
            "description": "Only templates in this folder.",
            "maxLength": 200,
            "type": "string"
          },
          "includeArchived": {
            "description": "Include archived templates. Default false.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "name": "list_templates"
    },
    {
      "description": "List the workspace's outbound event webhooks (GET /tenant-webhooks): url, subscribed events, enabled flag and delivery health (consecutiveFailures, lastError). Signing secrets are never returned.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "includeArchived": {
            "description": "Include deleted (archived) webhooks. Default false.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "name": "list_webhooks"
    },
    {
      "description": "Publish a template's current draft as its new live version (POST /templates/{id}/publish; write scope). Sends by templateId use it immediately. Fails if there is no draft or the template is archived.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "templateId": {
            "description": "The template id (UUID).",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "required": [
          "templateId"
        ],
        "type": "object"
      },
      "name": "publish_template"
    },
    {
      "description": "Read Ray's documentation as markdown. With no `slug` it returns the index (llms.txt) listing every page; then pass a page slug, i.e. the path after /docs/ without `.md`, such as `sending`, `templates`, `idempotency`, `errors`, `rate-limits`, `webhooks`, `status-and-feeds` or `channels/telegram`. Use it for anything the tool descriptions don't cover.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "slug": {
            "description": "Page slug like \"sending\" or \"channels/push-fcm\". Omit for the index of all pages.",
            "maxLength": 100,
            "type": "string"
          }
        },
        "type": "object"
      },
      "name": "read_docs"
    },
    {
      "description": "Send a notification (POST /send; needs a `write`-scoped key). Returns `{ sendId }` with HTTP 202: delivery is asynchronous, so check the outcome with get_send_status. Use exactly one mode:\n1. Single: `channelConfigId` + `recipient`.\n2. Fan-out: `channelConfigId` + `targets` (1-1000 `{ recipient, externalUserId? }`), one channel, one sendId.\n3. Multi-channel: `deliveries` (1-10, each with its own `channelConfigId`, `recipient` and `templateId` or `content`) for ONE person over several channels; add `feed` + `externalUserId` for a single in-app feed entry.\n4. Feed-only: `feed` (with `title`) + `externalUserId` and no channel: an in-app notification only.\nIn modes 1-2 give exactly one of `templateId` (a published template, plus `params`) or inline `content`; in mode 3 that choice is made per delivery. Get channel ids and each recipient shape from list_channels first. Pass `idempotencyKey` whenever a retry must not deliver twice.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "campaignId": {
            "description": "Free-form campaign id used to group click stats across sends.",
            "maxLength": 256,
            "minLength": 1,
            "type": "string"
          },
          "channelConfigId": {
            "description": "Modes 1-2: the channel to send through — an `id` from list_channels. Not allowed together with `deliveries`.",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          },
          "content": {
            "additionalProperties": {},
            "description": "Inline message content, used INSTEAD of `templateId` (give exactly one of the two). Shape by channel kind: ses_email / smtp_email → `{ subject, bodyHtml, bodyText }`; fcm_push → `{ title, body, imageUrl?, data?: { [key]: string } }`; slack_webhook → `{ text }` (Slack mrkdwn); discord_webhook → `{ content }` (max 2000 chars); telegram_bot → `{ text, disableLinkPreview? }` (Telegram HTML subset); twilio_sms / sendsms_mn → `{ text }` (plain text; up to 1600 chars on Twilio, sendsms_mn splits long text into SMS of 159 plain-Latin / 69 Cyrillic chars, each billed); generic_webhook → `{ title, body, data?: { [key]: string } }`. `{{name}}` placeholders are filled from `params` and escaped for the channel.",
            "propertyNames": {
              "type": "string"
            },
            "type": "object"
          },
          "deliveries": {
            "description": "Mode 3: the same notification to ONE person over several channels (e.g. push + email), max 10. Each entry has its own channel, recipient and content source. Do not combine with top-level channelConfigId/recipient/targets/templateId/content.",
            "items": {
              "properties": {
                "channelConfigId": {
                  "description": "Channel for this delivery (from list_channels).",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                  "type": "string"
                },
                "content": {
                  "additionalProperties": {},
                  "description": "Inline message content, used INSTEAD of `templateId` (give exactly one of the two). Shape by channel kind: ses_email / smtp_email → `{ subject, bodyHtml, bodyText }`; fcm_push → `{ title, body, imageUrl?, data?: { [key]: string } }`; slack_webhook → `{ text }` (Slack mrkdwn); discord_webhook → `{ content }` (max 2000 chars); telegram_bot → `{ text, disableLinkPreview? }` (Telegram HTML subset); twilio_sms / sendsms_mn → `{ text }` (plain text; up to 1600 chars on Twilio, sendsms_mn splits long text into SMS of 159 plain-Latin / 69 Cyrillic chars, each billed); generic_webhook → `{ title, body, data?: { [key]: string } }`. `{{name}}` placeholders are filled from `params` and escaped for the channel.",
                  "propertyNames": {
                    "type": "string"
                  },
                  "type": "object"
                },
                "logDescription": {
                  "description": "Feed/log description; only valid with inline `content`.",
                  "maxLength": 2000,
                  "type": "string"
                },
                "logTitle": {
                  "description": "Feed/log title; only valid with inline `content`.",
                  "maxLength": 500,
                  "type": "string"
                },
                "params": {
                  "additionalProperties": {},
                  "description": "Params for this delivery; falls back to the top-level `params`.",
                  "propertyNames": {
                    "type": "string"
                  },
                  "type": "object"
                },
                "recipient": {
                  "additionalProperties": {},
                  "description": "Channel-specific delivery target. Its shape depends on the kind of the chosen channel (list_channels returns each channel's exact `recipientSchema`): ses_email / smtp_email → `{ email, name?, cc?: [{ email, name? }], bcc?: [...], from?: { email?, name? } (per-send sender override; omitted fields fall back to the channel config; email must be on exactly the domain of the config fromAddress, subdomains rejected), replyTo?: [{ email, name? }], headers?: { \"X-Name\": \"value\" } (ASCII, max 15; From/To/Cc/Bcc/Reply-To/Subject etc. and provider controls such as X-SES-*/X-SMTPAPI/X-PM-*/X-Mailgun-* rejected), attachments?: [{ filename, content (base64), contentType?, disposition?: \"attachment\"|\"inline\", contentId? }] }`; fcm_push → `{ deviceToken }` OR `{ topic }` (exactly one; Ray keeps no device registry); telegram_bot → `{ chatId }` (numeric id as a string, or \"@channelname\"); twilio_sms → `{ phoneNumber }` (international E.164, e.g. \"+97699112233\"); sendsms_mn → `{ phoneNumber }` (Mongolian 8-digit, e.g. \"99112233\"); slack_webhook, discord_webhook, generic_webhook → `{}` (the destination is part of the channel config).",
                  "propertyNames": {
                    "type": "string"
                  },
                  "type": "object"
                },
                "templateId": {
                  "description": "Published template for this delivery. Exactly one of `templateId` or `content`.",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                  "type": "string"
                }
              },
              "required": [
                "channelConfigId",
                "recipient"
              ],
              "type": "object"
            },
            "maxItems": 10,
            "minItems": 1,
            "type": "array"
          },
          "externalUserId": {
            "description": "Your own id for the end user. Required with `feed`; keys their in-app feed.",
            "maxLength": 256,
            "minLength": 1,
            "type": "string"
          },
          "feed": {
            "description": "Create exactly ONE in-app feed entry for `externalUserId` (read back with list_feed_notifications). `title`/`description` default to the rendered template text; `title` is required for a feed-only send (mode 4).",
            "properties": {
              "description": {
                "maxLength": 2000,
                "type": "string"
              },
              "title": {
                "maxLength": 500,
                "type": "string"
              }
            },
            "type": "object"
          },
          "idempotencyKey": {
            "description": "Sent as the Idempotency-Key header. Retrying with the same key and same arguments returns the original sendId without sending again (24h window); the same key with different arguments fails with 409. Use a fresh unique value (e.g. a UUID) per logical notification.",
            "maxLength": 255,
            "minLength": 1,
            "type": "string"
          },
          "logDescription": {
            "description": "Feed/log description for inline `content` sends only. Placeholders allowed.",
            "maxLength": 2000,
            "type": "string"
          },
          "logTitle": {
            "description": "Feed/log title for inline `content` sends only (template sends take it from the template). Placeholders allowed.",
            "maxLength": 500,
            "type": "string"
          },
          "notBefore": {
            "description": "Schedule delivery for later: ISO 8601 datetime with offset, e.g. \"2026-09-15T09:00:00+08:00\". Omit to send now.",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "type": "string"
          },
          "params": {
            "additionalProperties": {},
            "description": "Values for the template's `{{placeholders}}`. Any JSON: strings, numbers, booleans, arrays and nested objects (arrays/objects drive `{{#section}}…{{/section}}` blocks). Missing required params are rejected with a 400 naming them.",
            "propertyNames": {
              "type": "string"
            },
            "type": "object"
          },
          "priority": {
            "description": "\"high\" (default) for transactional messages; \"low\" for marketing/bulk so it never delays transactional sends.",
            "enum": [
              "high",
              "low"
            ],
            "type": "string"
          },
          "recipient": {
            "additionalProperties": {},
            "description": "Mode 1: the single recipient. Mutually exclusive with `targets`. Channel-specific delivery target. Its shape depends on the kind of the chosen channel (list_channels returns each channel's exact `recipientSchema`): ses_email / smtp_email → `{ email, name?, cc?: [{ email, name? }], bcc?: [...], from?: { email?, name? } (per-send sender override; omitted fields fall back to the channel config; email must be on exactly the domain of the config fromAddress, subdomains rejected), replyTo?: [{ email, name? }], headers?: { \"X-Name\": \"value\" } (ASCII, max 15; From/To/Cc/Bcc/Reply-To/Subject etc. and provider controls such as X-SES-*/X-SMTPAPI/X-PM-*/X-Mailgun-* rejected), attachments?: [{ filename, content (base64), contentType?, disposition?: \"attachment\"|\"inline\", contentId? }] }`; fcm_push → `{ deviceToken }` OR `{ topic }` (exactly one; Ray keeps no device registry); telegram_bot → `{ chatId }` (numeric id as a string, or \"@channelname\"); twilio_sms → `{ phoneNumber }` (international E.164, e.g. \"+97699112233\"); sendsms_mn → `{ phoneNumber }` (Mongolian 8-digit, e.g. \"99112233\"); slack_webhook, discord_webhook, generic_webhook → `{}` (the destination is part of the channel config).",
            "propertyNames": {
              "type": "string"
            },
            "type": "object"
          },
          "showInFeed": {
            "description": "Legacy modes 1-2 flag: also add a feed entry for each target that has an externalUserId. Prefer `feed`. Not valid with `deliveries`.",
            "type": "boolean"
          },
          "targets": {
            "description": "Mode 2: fan-out to 1-1000 recipients over ONE channel, sharing one sendId. Mutually exclusive with `recipient`.",
            "items": {
              "properties": {
                "externalUserId": {
                  "description": "Your own id for this recipient. With `feed`/`showInFeed` they also get an in-app feed entry.",
                  "maxLength": 256,
                  "minLength": 1,
                  "type": "string"
                },
                "recipient": {
                  "additionalProperties": {},
                  "description": "Channel-specific delivery target. Its shape depends on the kind of the chosen channel (list_channels returns each channel's exact `recipientSchema`): ses_email / smtp_email → `{ email, name?, cc?: [{ email, name? }], bcc?: [...], from?: { email?, name? } (per-send sender override; omitted fields fall back to the channel config; email must be on exactly the domain of the config fromAddress, subdomains rejected), replyTo?: [{ email, name? }], headers?: { \"X-Name\": \"value\" } (ASCII, max 15; From/To/Cc/Bcc/Reply-To/Subject etc. and provider controls such as X-SES-*/X-SMTPAPI/X-PM-*/X-Mailgun-* rejected), attachments?: [{ filename, content (base64), contentType?, disposition?: \"attachment\"|\"inline\", contentId? }] }`; fcm_push → `{ deviceToken }` OR `{ topic }` (exactly one; Ray keeps no device registry); telegram_bot → `{ chatId }` (numeric id as a string, or \"@channelname\"); twilio_sms → `{ phoneNumber }` (international E.164, e.g. \"+97699112233\"); sendsms_mn → `{ phoneNumber }` (Mongolian 8-digit, e.g. \"99112233\"); slack_webhook, discord_webhook, generic_webhook → `{}` (the destination is part of the channel config).",
                  "propertyNames": {
                    "type": "string"
                  },
                  "type": "object"
                }
              },
              "required": [
                "recipient"
              ],
              "type": "object"
            },
            "maxItems": 1000,
            "minItems": 1,
            "type": "array"
          },
          "templateId": {
            "description": "Modes 1-2: a PUBLISHED template to render (see list_templates / publish_template). Exactly one of `templateId` or `content`. Its channelKind must match the channel's `templateKind`.",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          },
          "trackClicks": {
            "description": "Email channels only: rewrite links in bodyHtml so clicks are counted (read with get_click_stats). Default false.",
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "name": "send_notification"
    },
    {
      "description": "Send a real test of a template's PUBLISHED version to one recipient (POST /templates/{id}/test-send; write scope). It goes through the provider for real, so use a recipient the user controls. Test sends don't count toward the monthly quota (a small daily allowance applies instead) and never appear in feeds. Returns `{ sendId }` for get_send_status.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "channelConfigId": {
            "description": "Channel to test through (from list_channels); its templateKind must match the template's channelKind.",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          },
          "params": {
            "additionalProperties": {},
            "description": "Values for the template's `{{placeholders}}`. Any JSON: strings, numbers, booleans, arrays and nested objects (arrays/objects drive `{{#section}}…{{/section}}` blocks). Missing required params are rejected with a 400 naming them.",
            "propertyNames": {
              "type": "string"
            },
            "type": "object"
          },
          "recipient": {
            "additionalProperties": {},
            "description": "Channel-specific delivery target. Its shape depends on the kind of the chosen channel (list_channels returns each channel's exact `recipientSchema`): ses_email / smtp_email → `{ email, name?, cc?: [{ email, name? }], bcc?: [...], from?: { email?, name? } (per-send sender override; omitted fields fall back to the channel config; email must be on exactly the domain of the config fromAddress, subdomains rejected), replyTo?: [{ email, name? }], headers?: { \"X-Name\": \"value\" } (ASCII, max 15; From/To/Cc/Bcc/Reply-To/Subject etc. and provider controls such as X-SES-*/X-SMTPAPI/X-PM-*/X-Mailgun-* rejected), attachments?: [{ filename, content (base64), contentType?, disposition?: \"attachment\"|\"inline\", contentId? }] }`; fcm_push → `{ deviceToken }` OR `{ topic }` (exactly one; Ray keeps no device registry); telegram_bot → `{ chatId }` (numeric id as a string, or \"@channelname\"); twilio_sms → `{ phoneNumber }` (international E.164, e.g. \"+97699112233\"); sendsms_mn → `{ phoneNumber }` (Mongolian 8-digit, e.g. \"99112233\"); slack_webhook, discord_webhook, generic_webhook → `{}` (the destination is part of the channel config).",
            "propertyNames": {
              "type": "string"
            },
            "type": "object"
          },
          "templateId": {
            "description": "The template id (UUID).",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "required": [
          "templateId",
          "channelConfigId",
          "recipient"
        ],
        "type": "object"
      },
      "name": "test_send_template"
    },
    {
      "description": "Restore an archived template (POST /templates/{id}/unarchive; write scope). This is how to reclaim a name held by an archived template instead of creating a new one.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "templateId": {
            "description": "The template id (UUID).",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "required": [
          "templateId"
        ],
        "type": "object"
      },
      "name": "unarchive_template"
    },
    {
      "description": "Replace a template's draft (PATCH /templates/{id}; write scope). This is a full replacement, not a partial patch: pass content, logTitle, logDescription and paramOverrides as they should end up (get_template first). `name`, `folder` and `channelKind` are required for validation but not changed (channelKind must equal the original). Live sends keep using the published version until you publish, either with `publish: true` here or publish_template. Fails on archived templates.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "channelKind": {
            "description": "Which channel family the template renders for. Must match the `templateKind` of the channels you will send through (list_channels). Can't be changed after creation.",
            "enum": [
              "email_html",
              "fcm_basic",
              "slack_text",
              "discord_text",
              "telegram_text",
              "sms_text",
              "webhook_json"
            ],
            "type": "string"
          },
          "content": {
            "additionalProperties": {},
            "description": "Channel-shaped template body; the shape is fixed by `channelKind`: email_html → `{ subject, bodyHtml, bodyText }`; fcm_basic → `{ title, body, imageUrl?, data?: { [key]: string } }`; slack_text → `{ text }`; discord_text → `{ content }` (max 2000 chars); telegram_text → `{ text, disableLinkPreview? }`; sms_text → `{ text }` (plain text, max 1600; long text is split into several billed SMS — on sendsms_mn into parts of 159 plain-Latin / 69 Cyrillic chars); webhook_json → `{ title, body, data?: { [key]: string } }`. Use `{{name}}` placeholders and `{{#items}}…{{/items}}` sections; the required params are derived from them automatically.",
            "propertyNames": {
              "type": "string"
            },
            "type": "object"
          },
          "folder": {
            "description": "Optional folder path for organisation, e.g. \"billing\".",
            "maxLength": 200,
            "type": "string"
          },
          "logDescription": {
            "description": "One-line description for the in-app feed and delivery logs. Placeholders allowed.",
            "maxLength": 2000,
            "minLength": 1,
            "type": "string"
          },
          "logTitle": {
            "description": "Short title shown in the in-app feed and delivery logs, e.g. \"Invoice {{number}} is due\". Placeholders allowed.",
            "maxLength": 500,
            "minLength": 1,
            "type": "string"
          },
          "name": {
            "description": "Template name, unique per workspace (archived templates included).",
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "paramOverrides": {
            "additionalProperties": {
              "properties": {
                "description": {
                  "maxLength": 500,
                  "type": "string"
                },
                "optional": {
                  "type": "boolean"
                }
              },
              "type": "object"
            },
            "description": "Per-param metadata keyed by param name: `{ optional?: boolean, description?: string }`. Mark a placeholder optional so sends may omit it.",
            "propertyNames": {
              "type": "string"
            },
            "type": "object"
          },
          "publish": {
            "description": "true = publish this version immediately so sends by templateId use it. Default false (saved as draft).",
            "type": "boolean"
          },
          "templateId": {
            "description": "The template id (UUID).",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "required": [
          "templateId",
          "name",
          "channelKind",
          "content",
          "logTitle",
          "logDescription"
        ],
        "type": "object"
      },
      "name": "update_template_draft"
    },
    {
      "description": "Change a webhook (PATCH /tenant-webhooks/{id}; write scope). Only the fields you pass change; pass at least one. `rotateSecret: true` issues a new signing secret, returned once in the response, and the old secret stops verifying immediately, so the receiving service must be updated at the same time.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "enabled": {
            "description": "false pauses deliveries without deleting the webhook.",
            "type": "boolean"
          },
          "events": {
            "description": "Replaces the subscribed events. Events to receive: \"notification.delivered\", \"notification.failed_terminal\" (one per recipient), and \"send.completed\" (once per send, with status totals).",
            "items": {
              "enum": [
                "notification.delivered",
                "notification.failed_terminal",
                "send.completed"
              ],
              "type": "string"
            },
            "minItems": 1,
            "type": "array"
          },
          "name": {
            "description": "New label.",
            "maxLength": 100,
            "minLength": 1,
            "type": "string"
          },
          "rotateSecret": {
            "description": "true rotates the signing secret (new secret returned once; old one invalid immediately).",
            "type": "boolean"
          },
          "url": {
            "description": "New public HTTPS endpoint.",
            "type": "string"
          },
          "webhookId": {
            "description": "The webhook id (UUID).",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "type": "string"
          }
        },
        "required": [
          "webhookId"
        ],
        "type": "object"
      },
      "name": "update_webhook"
    },
    {
      "description": "Identify the API key this connection uses (GET /me): `tenantId` (the workspace), `apiKeyId` and `scopes`. Write tools need the `write` scope; call this to check before attempting them.",
      "inputSchema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {},
        "type": "object"
      },
      "name": "whoami"
    }
  ]
}

What the leaf hashes

{"body":"541eef29417c9ec07dc90c700747211d882408a5a1106b860cb81c3a8d72c233","id":221267,"kind":"mcp.tools","method":"probe.mcp/1","observed_us":1793027234340786,"prev":null,"subject":"9e97907e-8c2c-4962-836f-58914a77ab83","subject_kind":"endpoint"}

Proof

Its day has not closed yet. A day closes ten minutes after midnight UTC; the proof appears here then.

How to check it · This row as JSON