Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6175455
build(deps): bump docker/login-action from 4.4.0 to 4.5.1
dependabot[bot] Jul 28, 2026
c25a383
fix(webchat): translate hardcoded Chinese copy
TheTrigger Jul 28, 2026
a84b8d5
Merge pull request #195 from TheTrigger/fix/translate-webchat-copy-to…
Telli Jul 28, 2026
c629eb3
docs(spec): add MCP csharp-sdk v2.0 migration design (zh-CN)
geffzhang Jul 29, 2026
3dc05b1
build(mcp): upgrade csharp-sdk packages to 2.0.0
geffzhang Jul 29, 2026
27058e9
feat(mcp): add gateway v2 compatibility config and defaults
geffzhang Jul 29, 2026
0c61f1d
fix(mcp-proxy): align proxy error semantics with actionable app-id di…
geffzhang Jul 29, 2026
8424e18
fix(mcp): enforce inputSchema and align discover-first tests
geffzhang Jul 29, 2026
ac4ee19
feat(mcpapp): enforce strict tool schema handling in app enumeration
geffzhang Jul 29, 2026
6eb5f26
feat(client-mcp): add discover support with protocol-aware fallback
geffzhang Jul 29, 2026
93f2ab6
feat(gateway-mcp): enable tasks extension and document v2 compatibili…
geffzhang Jul 29, 2026
b451088
docs(mcp): 同步 MCP v2 文档(中英文)
geffzhang Jul 29, 2026
aafd307
docs: add MCP csharp-sdk v2 upgrade plan
geffzhang Jul 29, 2026
5c253c5
Sanitize MCP app tool names for LLM safety
geffzhang Jul 30, 2026
f59f674
fix: address all 4 review comments from PR #196 review thread
Copilot Jul 30, 2026
c02e5f2
mcp v2 compatibility hardening and docs alignment
geffzhang Jul 30, 2026
2909e1c
Fix MCP app test tool name expectations
geffzhang Jul 30, 2026
45a6706
Refresh MiniMax model profile capabilities and endpoints
octo-patch Jul 31, 2026
19448ca
feat(telegram): add long-polling transport
TheTrigger Jul 30, 2026
0588ce5
Merge branch 'main' into dependabot/github_actions/docker/login-actio…
Telli Aug 1, 2026
35451cc
fix(telegram): centralize update mode handling
Telli Aug 1, 2026
47aa996
fix(mcp): align client with 2026 protocol metadata
Telli Aug 1, 2026
a8da0f7
Merge pull request #194 from clawdotnet/dependabot/github_actions/doc…
Telli Aug 1, 2026
44725f9
Merge branch 'main' into octo/20260731-parameter-refresh-recvqh5jpuhrP1
Telli Aug 1, 2026
a858960
test(mcp): make fallback response ownership explicit
Telli Aug 1, 2026
1555af9
Merge pull request #198 from octo-patch/octo/20260731-parameter-refre…
Telli Aug 1, 2026
634d81c
Merge branch 'main' into feature/telegram-long-polling
Telli Aug 1, 2026
3876976
Merge pull request #197 from TheTrigger/feature/telegram-long-polling
Telli Aug 1, 2026
d112a07
Merge branch 'main' into mcp2upragde
Telli Aug 1, 2026
e8973df
Merge pull request #196 from clawdotnet/mcp2upragde
Telli Aug 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ jobs:
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c

- name: Log in to GitHub Container Registry
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand Down
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@

All notable changes to this project are tracked in this file.

## [Unreleased] - 2026-03-04
## [Unreleased]

### MCP v2 Upgrade

- Upgraded MCP integration surfaces to C# SDK `2.0.0` behavior across gateway, MCP App, and client-facing SDK helpers.
- Enforced stricter MCP App tool schema handling: tools with missing/defaulted `inputSchema` are skipped during enumeration instead of silently normalizing to `{}`.
- Added MCP App regression coverage for missing schema tool descriptors and preserved structured-content suppression behavior.
- Added `OpenClawHttpClient.DiscoverMcpAsync` and MCP discover models with protocol-aware fallback to initialize-first servers.
- Added `McpCallToolResult.StructuredContent` to `OpenClaw.Client` models for v2 response parity.
- Registered MCP Tasks extension on gateway MCP server via `ModelContextProtocol.Extensions.Tasks` with in-memory task store.
- Added gateway MCP proxy regression test to validate MCP server availability after Tasks extension enablement.

### Integration API, MCP, and SDK

Expand Down
29 changes: 18 additions & 11 deletions OpenClaw.Net.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,44 @@
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Folder Name="/mcpapp/">
<Project Path="src/mcpapp/OpenClaw.McpApp/OpenClaw.McpApp.csproj" />
</Folder>
<Folder Name="/mcpapp/test-apps/" />
<Folder Name="/mcpapp/test-apps/GroceryInventory/">
<File Path="src/mcpapp/test-apps/GroceryInventory/openclaw.mcpapp.json" />
</Folder>
<Folder Name="/samples/">
<Project Path="samples/OpenClaw.DurableAgentReview/OpenClaw.DurableAgentReview.csproj" />
<Project Path="samples/OpenClaw.HelloAgent/OpenClaw.HelloAgent.csproj" />
<Project Path="samples/OpenClaw.MicrosoftExtensionsAIProvider/OpenClaw.MicrosoftExtensionsAIProvider.csproj" />
<Project Path="samples/OpenClaw.SemanticKernelInteropHost/OpenClaw.SemanticKernelInteropHost.csproj" />
</Folder>
<Folder Name="/src/">
<Project Path="src/OpenClaw.Client/OpenClaw.Client.csproj" />
<Project Path="src/OpenClaw.Cli/OpenClaw.Cli.csproj" />
<Project Path="src/OpenClaw.Client/OpenClaw.Client.csproj" />
<Project Path="src/OpenClaw.Companion/OpenClaw.Companion.csproj" />
<Project Path="src/OpenClaw.Dashboard/OpenClaw.Dashboard.csproj" />
<Project Path="src/OpenClaw.MicrosoftAgentFrameworkAdapter/OpenClaw.MicrosoftAgentFrameworkAdapter.csproj" />
<Project Path="src/OpenClaw.Payments.Abstractions/OpenClaw.Payments.Abstractions.csproj" />
<Project Path="src/OpenClaw.Payments.Core/OpenClaw.Payments.Core.csproj" />
<Project Path="src/OpenClaw.Payments.StripeLink/OpenClaw.Payments.StripeLink.csproj" />
<Project Path="src/OpenClaw.Providers.MicrosoftExtensionsAI/OpenClaw.Providers.MicrosoftExtensionsAI.csproj" />
<Project Path="src/OpenClaw.PluginKit/OpenClaw.PluginKit.csproj" />
<Project Path="src/OpenClaw.Plugins.Mempalace/OpenClaw.Plugins.Mempalace.csproj" />
<Project Path="src/OpenClaw.Plugins.Payment/OpenClaw.Plugins.Payment.csproj" />
<Project Path="src/OpenClaw.Plugins.TokenJuice/OpenClaw.Plugins.TokenJuice.csproj" />
<Project Path="src/OpenClaw.Protocols.Browser/OpenClaw.Protocols.Browser.csproj" />
<Project Path="src/OpenClaw.Protocols.Mqtt/OpenClaw.Protocols.Mqtt.csproj" />
<Project Path="src/OpenClaw.Plugins.Payment/OpenClaw.Plugins.Payment.csproj" />
<Project Path="src/OpenClaw.Plugins.Mempalace/OpenClaw.Plugins.Mempalace.csproj" />
<Project Path="src/OpenClaw.Dashboard/OpenClaw.Dashboard.csproj" />
<Project Path="src/OpenClaw.MicrosoftAgentFrameworkAdapter/OpenClaw.MicrosoftAgentFrameworkAdapter.csproj" />
<Project Path="src/OpenClaw.SkillKit.Abstractions/OpenClaw.SkillKit.Abstractions.csproj" />
<Project Path="src/OpenClaw.SkillKit/OpenClaw.SkillKit.csproj" />
<Project Path="src/OpenClaw.Providers.MicrosoftExtensionsAI/OpenClaw.Providers.MicrosoftExtensionsAI.csproj" />
<Project Path="src/OpenClaw.Routing.Onnx/OpenClaw.Routing.Onnx.csproj" />
<Project Path="src/OpenClawNet.Sandbox.OpenSandbox/OpenClawNet.Sandbox.OpenSandbox.csproj" />
<Project Path="src/OpenClaw.PluginKit/OpenClaw.PluginKit.csproj" />
<Project Path="src/OpenClaw.SemanticKernelAdapter/OpenClaw.SemanticKernelAdapter.csproj" />
<Project Path="src/OpenClaw.Tui/OpenClaw.Tui.csproj" />
<Project Path="src/OpenClaw.SkillKit.Abstractions/OpenClaw.SkillKit.Abstractions.csproj" />
<Project Path="src/OpenClaw.SkillKit/OpenClaw.SkillKit.csproj" />
<Project Path="src/OpenClaw.Testing/OpenClaw.Testing.csproj" />
<Project Path="src/OpenClaw.TestPluginFixtures/OpenClaw.TestPluginFixtures.csproj" />
<Project Path="src/OpenClaw.Tui/OpenClaw.Tui.csproj" />
<Project Path="src/OpenClaw.WhatsApp.BaileysWorker/OpenClaw.WhatsApp.BaileysWorker.csproj" />
<Project Path="src/OpenClawNet.Sandbox.OpenSandbox/OpenClawNet.Sandbox.OpenSandbox.csproj" />
</Folder>
<Project Path="src/OpenClaw.Agent/OpenClaw.Agent.csproj" />
<Project Path="src/OpenClaw.Channels/OpenClaw.Channels.csproj" />
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ The public documentation site is **[AgentQi.dev](https://agentqi.dev)**. The sou
| [docs/mempalace-memory.md](docs/mempalace-memory.md) | Optional MemPalace.NET memory provider and temporal knowledge graph |
| [docs/CANVAS_A2UI.md](docs/CANVAS_A2UI.md) | Supported Canvas and A2UI visual workspace behavior |
| [docs/MODEL_PROFILES.md](docs/MODEL_PROFILES.md) | Provider-agnostic named model profiles (including Gemma) |
| [docs/TELEGRAM.md](docs/TELEGRAM.md) | Telegram webhook and long-polling channel setup |
| [docs/deployment/TAILSCALE.md](docs/deployment/TAILSCALE.md) | Optional Tailscale Serve private access |
| [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md) | Supported upstream skill, plugin, and channel surface |
| [docs/zh-CN/START_HERE.md](docs/zh-CN/START_HERE.md) | Simplified Chinese first-run orientation |
Expand Down
20 changes: 20 additions & 0 deletions docs/COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,26 @@ This is the canonical compatibility matrix for OpenClaw.NET. It covers runtime m
- `OpenClaw:Runtime:Mode=aot` forces the strict trim-safe lane even on a JIT-capable build.
- `OpenClaw:Runtime:Mode=jit` enables the expanded plugin and dynamic-native lane.

## MCP v2 Compatibility (C# SDK 2.0.0)

OpenClaw.NET now targets `ModelContextProtocol` / `ModelContextProtocol.AspNetCore` `2.0.0` and enables the Tasks extension on the gateway MCP server.

| Surface | Status | Notes |
| --- | --- | --- |
| Gateway `/mcp` on MCP v2 SDK | Supported | Gateway MCP services run on `ModelContextProtocol.AspNetCore` `2.0.0` with stateless transport by default. |
| MCP Tasks extension | Supported | Registered via `ModelContextProtocol.Extensions.Tasks` with in-memory task store for async task operations. |
| Discover-first negotiation | Supported with caveats | Default path is discover-first; legacy clients can still initialize through compatibility handling. |
| Required `inputSchema` handling | Supported with caveats | Tool schema validation is strict by default; missing/defaulted schema is skipped on MCP App enumeration. |

Compatibility switches (Gateway config):

- `OpenClaw:McpCompatibility:ForceLegacyInitialize=true`
- Forces legacy initialize-first behavior (disables stateless discover-first mode).
- `OpenClaw:McpCompatibility:AllowRelaxedInputSchemaValidation=true`
- Reserved flag only. It is not currently wired into MCP App enumeration; with the current SDK, omitted `inputSchema` may still surface as synthesized `{"type":"object"}` on that path.
- `OpenClaw:McpCompatibility:EnableDiscoveryFirst=true`
- Keeps discover-first path enabled (default).

## Upstream Skill Compatibility

| Surface | Status | Notes |
Expand Down
23 changes: 22 additions & 1 deletion docs/MCPAPP.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

MCP App is the module that treats third-party MCP applications as first-class citizens in OpenClaw.NET. It provides manifest-based automatic discovery, lifecycle and process management, and tool bridging — letting MCP applications be installed, discovered, and used much like VS Code extensions.

OpenClaw integrates with the MCP App ecosystem MCP-first, compatible with the [Model Context Protocol](https://modelcontextprotocol.io/) 2025-03-26 spec and the `text/html;profile=mcp-app` interactive UI specification.
OpenClaw integrates with the MCP App ecosystem MCP-first, compatible with the [Model Context Protocol](https://modelcontextprotocol.io/) and the `text/html;profile=mcp-app` interactive UI specification. The runtime now uses MCP C# SDK `2.0.0` on gateway and MCP App surfaces.

## How It Differs from Existing MCP Integrations

Expand All @@ -28,6 +28,7 @@ MCP App is designed for MCP applications that need to be **packaged and distribu
- Six-stage lifecycle tracking (Discovered → Validated → Loaded → Running → Stopped → Failed)
- Seamless integration with `NativePluginRegistry` — discovered tools become immediately available to the Agent
- Browser host endpoints that let an MCP App UI reuse the same connected upstream MCP session as the Agent
- MCP v2-compatible schema handling: tools missing a usable `inputSchema` are skipped during MCP App enumeration

## Browser Host Endpoints

Expand Down Expand Up @@ -61,6 +62,13 @@ This is the bridge that lets a rich MCP App UI and the Agent collaborate against
- UI tools with `_meta.ui.resourceUri` suppress `structuredContent` when results flow back into the model, but the browser-side MCP proxy still forwards normal MCP responses unchanged.
- For cross-origin browser MCP clients, OpenClaw allows the MCP Streamable HTTP headers `mcp-protocol-version` and `Mcp-Session-Id` in CORS.

### MCP v2 Behavior Notes

- Gateway MCP server runs on `ModelContextProtocol.AspNetCore` `2.0.0` and enables the MCP Tasks extension.
- The default transport posture is stateless/discover-first, with compatibility switches in `OpenClaw:McpCompatibility`.
- `server/discover` availability can vary by negotiated protocol revision; OpenClaw client integrations use protocol-aware fallback behavior to remain compatible with initialize-first servers.
- For MCP App tool onboarding, schema validation is strict by default: if a tool has no usable `inputSchema`, it is not registered into the model-visible tool set.

## Architecture

```
Expand Down Expand Up @@ -214,6 +222,18 @@ Add to `appsettings.json`:
| `Deny` | `[]` | Denylist, deny takes precedence over allow |
| `Entries` | `{}` | Per-App fine-grained overrides |

### MCP Compatibility Switches (Gateway)

These settings live under `OpenClaw:McpCompatibility` and affect gateway MCP behavior (not per-app manifest parsing):

| Field | Default | Description |
|-------|---------|-------------|
| `EnableDiscoveryFirst` | `true` | Enables discover-first negotiation for MCP v2 clients. |
| `ForceLegacyInitialize` | `false` | Forces initialize-first behavior for legacy compatibility. |
| `RequireOAuthIssuerValidation` | `true` | Enforces strict OAuth issuer checks for secured deployments. |
| `RequirePkceS256` | `true` | Requires PKCE S256 where OAuth flows are used. |
| `AllowRelaxedInputSchemaValidation` | `false` | Reserved flag only; MCP App enumeration does not currently consult it, and SDK-normalized omitted schemas may still appear as `{"type":"object"}`. |

### Entries Overrides

`Entries` can override these manifest fields per App ID:
Expand Down Expand Up @@ -253,6 +273,7 @@ Every tool discovered from an MCP App is registered into the `NativePluginRegist
- **Tool name prefix**: automatically applied from the manifest's `toolNamePrefix`
- **Visibility handling**: tools marked with MCP App visibility metadata such as `"ui": { "visibility": ["app"] }` stay available to the browser host but are not registered as model-visible Agent tools
- **Error handling**: invalid JSON arguments and remote execution errors return result text prefixed with `Error:`
- **Schema strictness**: tools with missing/defaulted `inputSchema` are skipped at enumeration time instead of silently normalized
- **Structured output**: supports both text content and structured content from MCP tools, except that UI tools suppress `structuredContent` when OpenClaw feeds results back into the model

```csharp
Expand Down
29 changes: 29 additions & 0 deletions docs/PRODUCTION_FIXES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,35 @@ All identified production issues have been resolved. The codebase is now product

---

## MCP v2 Migration Guardrails

**Status**: ✅ **AVAILABLE**

### Scope

Gateway and MCP App paths now run on MCP C# SDK `2.0.0` with stricter schema behavior and discover-first transport defaults.

### Operator Fallback Switches

- `OpenClaw:McpCompatibility:ForceLegacyInitialize=true`
- Use when a client cannot negotiate discover-first/stateless flow.
- `OpenClaw:McpCompatibility:AllowRelaxedInputSchemaValidation=true`
- Reserved flag only. It is not currently wired into MCP App enumeration; with the current SDK, omitted `inputSchema` may still surface as synthesized `{"type":"object"}` on that path.

### Suggested Incident Procedure

1. Detect: watch MCP errors for schema/revision negotiation failures.
2. Contain: enable only the minimum fallback switch needed.
3. Verify: run MCP smoke (`initialize`, `tools/list`, `tools/call`) against `/mcp` and `/apps/mcp/{appId}`.
4. Recover: remove fallback switches after upstream/client fix lands.

### Safety Notes

- Keep fallback windows short and auditable.
- Do not broaden relaxed schema behavior globally without explicit incident justification.

---

## Critical Fix: Missing FileMemoryStore Implementation

**Status**: ✅ **RESOLVED**
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Use this page as the map. If you are unsure where to go next, the groups below a
| [RELEASES.md](RELEASES.md) | Desktop download bundles, release assets, and signing/notarization status. |
| [AgentQi Mobile](https://github.com/agentqi/agentqi-mobile) | Open-source .NET MAUI Android companion for monitoring and operating an OpenClaw.NET gateway; includes build-from-source instructions. |
| [TOOLS_GUIDE.md](TOOLS_GUIDE.md) | Native and optional tool catalog, behavior, and configuration. |
| [MCPAPP.md](MCPAPP.md) | MCP App manifest discovery, lifecycle management, tool bridging, resources, prompts, and UI bundles. |
| [MCPAPP.md](MCPAPP.md) | MCP App manifest discovery, lifecycle management, tool bridging, MCP v2 compatibility (discover/tasks/schema), resources, prompts, and UI bundles. |
| [tokenjuice.md](tokenjuice.md) | Deterministic, rule-driven tool output reduction before tool results enter model context. |
| [GOAL_TECHNICAL_ARCHITECTURE.md](GOAL_TECHNICAL_ARCHITECTURE.md) | Session-scoped `/goal` auto-continuation architecture, states, tools, budget limits, and runtime integration. |
| [LOOP_TECHNICAL_ARCHITECTURE.md](LOOP_TECHNICAL_ARCHITECTURE.md) | `/loop` recurring-prompt command architecture, scheduler behavior, control tool, and termination model. |
Expand Down
71 changes: 71 additions & 0 deletions docs/TELEGRAM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Telegram channel

OpenClaw.NET supports two mutually exclusive inbound delivery modes for Telegram:

- `webhook` (default): Telegram sends updates to the gateway through a public HTTPS endpoint.
- `long-polling`: the gateway fetches updates from Telegram with `getUpdates`; only outbound HTTPS access is required.

Both modes use the same Telegram update handler, allowlist, recent-sender store, message pipeline, and session/memory model.

## Upgrade compatibility

Webhook remains the default. Existing Telegram configurations that do not contain `UpdateMode` continue to register and use the same webhook endpoint, path, signature validation, and secret settings after upgrading.

Long polling is opt-in only: set `UpdateMode` to `long-polling` explicitly. The gateway never calls `deleteWebhook` for an existing configuration unless long polling has been selected.

## Long polling

Long polling is a convenient choice for a single gateway running behind NAT, on a workstation, or on a private home server:

```json
{
"OpenClaw": {
"Channels": {
"Telegram": {
"Enabled": true,
"BotTokenRef": "env:TELEGRAM_BOT_TOKEN",
"UpdateMode": "long-polling",
"PollingTimeoutSeconds": 30,
"PollingRetryDelaySeconds": 5,
"DropPendingUpdatesOnStart": false
}
}
}
}
```
The gateway calls `deleteWebhook` before polling because Telegram does not allow `getUpdates` while a webhook is configured. Pending updates are preserved by default. Set `DropPendingUpdatesOnStart` to `true` only when intentionally discarding the existing queue.

Only one active long-poll consumer should use a bot token. The adapter processes updates sequentially and advances the `offset` after each handled update.

Temporary failures use bounded exponential backoff. Telegram `retry_after` values are honored for rate limits, conflicts report that another poller may be active, and permanent authentication or request errors stop the channel with an actionable error instead of retrying indefinitely.

The CLI can configure this mode without a public URL or webhook secret:

```bash
openclaw setup channel telegram \
--non-interactive \
--bot-token-ref env:TELEGRAM_BOT_TOKEN \
--update-mode long-polling
```

## Webhook

Webhook mode requires a public HTTPS URL reachable by Telegram. The gateway itself does not need to own a public IP when a reverse proxy or tunnel supplies that endpoint.

```json
{
"OpenClaw": {
"Channels": {
"Telegram": {
"Enabled": true,
"BotTokenRef": "env:TELEGRAM_BOT_TOKEN",
"UpdateMode": "webhook",
"WebhookPublicBaseUrl": "https://bot.example.com",
"WebhookPath": "/telegram/inbound",
"ValidateSignature": true,
"WebhookSecretTokenRef": "env:TELEGRAM_WEBHOOK_SECRET"
}
}
}
}
```
6 changes: 3 additions & 3 deletions docs/USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -733,14 +733,14 @@ openclaw migrate upstream \

Dry-run is the default. `--apply` writes the translated config, imports managed `SKILL.md` packages, and writes the plugin review plan next to the target config.

### Webhook Channels
### Channel Setup
You can configure OpenClaw to listen to messages in the background natively.
Enable them under the `Channels` block in your config.

- **Telegram**: Basic bot API support.
- **Telegram**: Bot API support through a public HTTPS webhook or outbound-only long polling. See [TELEGRAM.md](TELEGRAM.md).
- **Twilio SMS**: SMS support via Twilio.
- **WhatsApp**: Official Cloud API or custom bridge support.
- Setup walkthroughs: `../README.md#telegram-webhook-channel` and `../README.md#twilio-sms-channel`.
- Setup walkthroughs: [Telegram](TELEGRAM.md) and the channel configuration in `appsettings.json`.

### Recipient IDs (Telegram / SMS / Email)
Scheduled jobs (Cron) and outbound delivery require a `RecipientId` that is specific to each channel:
Expand Down
Loading
Loading