{"id":"add-resend","name":"add-resend","summary":"Chat SDKを通じてResend(メール)チャネル連携を追加してください。","body":"# Add Resend Email Channel\n\nConnect NanoClaw to email via Resend for async email conversations. NanoClaw\ndoesn't ship channels in trunk — this skill copies the Resend adapter in from the\n`channels` branch.\n\nThe mechanical steps under **Apply** carry `nc:` directive fences: an agent reads\nthe prose and applies them, and a parser can apply them deterministically from\nthe same document. Every directive is idempotent, so the whole skill is safe to\nre-run; anything a parser can't apply falls back to the prose beside it.\n\n## Apply\n\n### 1. Copy the adapter\n\nFetch the `channels` branch and copy the Resend adapter into `src/channels/`\n(overwrite — the branch is canonical):\n\n```nc:copy from-branch:channels\nsrc/channels/resend.ts\nsrc/channels/resend-registration.test.ts\n```\n\n### 2. Register the adapter\n\nAppend the self-registration import to the channel barrel (skipped if the line\nis already present). This one line is the skill's only reach-in into core:\n\n```nc:append to:src/channels/index.ts\nimport './resend.js';\n```\n\n### 3. Install the adapter package\n\nPinned to an exact version — the supply-chain policy rejects ranges and `latest`:\n\n```nc:dep\n@resend/chat-sdk-adapter@0.1.1\n```\n\n### 4. Build and validate\n\nBuild guards the typed `createChatSdkBridge(...)` core call and proves the\ndependency is installed (the adapter imports `@resend/chat-sdk-adapter`; if it\nisn't installed the barrel throws). End-to-end email delivery against a real\ndomain is verified manually once the service runs.\n\n```nc:run effect:build\npnpm run build\n```\n```nc:run effect:test\npnpm exec vitest run src/channels/resend-registration.test.ts\n```\n\n`resend-registration.test.ts` imports the real channel barrel and asserts the\nregistry contains `resend`. It goes red if the import line is deleted or drifts,\nif the barrel fails to evaluate, or if `@resend/chat-sdk-adapter` isn't installed\n(the import throws) — so it also covers the dependency from step 3.\n\n## Credentials\n\nResend account and domain setup is human and interactive — these steps are\nprose, not directives (no parser can verify a sending domain or click through the\nResend UI). A recipe rebuild produces a compiling, registered adapter that cannot\nreceive a message until they're done.\n\n1. Go to [resend.com](https://resend.com) and create an account.\n2. Add and verify your sending domain.\n3. Go to **API Keys** and create a new key.\n4. Set up a webhook:\n   - Go to **Webhooks** > **Add webhook**.\n   - URL: `https://your-domain/webhook/resend`.\n   - Events: select **email.received**.\n   - Copy the signing secret.\n\n### Store the credentials\n\nCapture the secrets, then write them. `prompt` only *asks* and binds the answer\nto a name; a separate directive consumes it — so the same prompts could feed\n`ncl` or the OneCLI vault instead of `.env` by swapping only the consumer. Here\nthey go to `.env` (set-if-absent — a value you've already filled in is never\noverwritten):\n\n```nc:prompt api_key secret\nPaste the Resend API key — API Keys, starts with `re_`.\n```\n```nc:prompt webhook_secret secret\nPaste the webhook signing secret — Webhooks, the value you copied above.\n```\n```nc:prompt from_address\nThe bot's sending email address on your verified domain (e.g. `bot@yourdomain.com`).\n```\n```nc:prompt from_name\nThe display name to send as (e.g. `NanoClaw`).\n```\n```nc:env-set\nRESEND_API_KEY={{api_key}}\nRESEND_FROM_ADDRESS={{from_address}}\nRESEND_FROM_NAME={{from_name}}\nRESEND_WEBHOOK_SECRET={{webhook_secret}}\n```\n## Connect yourself\n\nBecause email is direct-addressable, the bot can write to you first — so wire\nyour own address as the owner and have it email you a hello. Tell it your address\nand which agent should answer your email (`ncl groups list` shows their folders):\n\n```nc:prompt owner_email\nYour email address — I'll wire you as owner and email you a hello.\n```\n```nc:prompt agent_folder\nWhich agent should answer your email? Enter its folder (run `ncl groups list`).\n```\n\nRegister yourself as the owner, wire your address so the agent answers your email,\nand send the hello:\n\n```nc:run effect:wire\nncl users create --id resend:{{owner_email}} --kind resend --display-name Owner\nncl roles grant --user resend:{{owner_email}} --role owner\nncl messaging-groups create --channel-type resend --platform-id resend:{{owner_email}} --is-group 0\nncl wirings create --channel-type resend --platform-id resend:{{owner_email}} --agent-group {{agent_folder}} --engage-mode pattern --engage-pattern .\nncl messaging-groups send --channel-type resend --platform-id resend:{{owner_email}} --sender-id resend:{{owner_email}} --sender Owner --text \"Hi — I'm your NanoClaw assistant, reachable by email now. Reply to this thread anytime.\"\n```\n\nThe hello arrives as a fresh email thread; reply to keep the conversation going.\nYour own address is the conversation key (`resend:<your-address>`).\n\n## Next Steps\n\nIf you're in the middle of `/setup`, return to the setup flow now. (Answering an\n*open* inbox — anyone who emails in, not just you — is a separate, not-yet-wired\ncase: email is plain-message, so the router never auto-creates a group for an\nunknown sender; each correspondent's `resend:<their-address>` must be wired\nexplicitly.)\n\n## Channel Info\n\n- **type**: `resend`\n- **terminology**: Resend handles email. The bot has one fixed sending identity (`RESEND_FROM_ADDRESS`, e.g. `bot@yourdomain.com`); every *external correspondent* the bot emails with is a separate conversation, keyed by *their* address.\n- **how-to-find-id**: The platform ID is the **correspondent's** email address, prefixed — `resend:<their-address>` (e.g. `resend:you@example.com`) — **not** the bot's from-address. The adapter derives it from the reply-to party (`channelIdFromThreadId` returns `resend:<address>`); each distinct email thread from that person (by root `Message-ID`) is a sub-conversation under it.\n- **supports-threads**: no (the adapter sets `supportsThreads: false`; replies still thread via email headers, but the router does not treat threads as the primary conversation unit)\n- **typical-use**: Async communication -- email conversations with longer response expectations\n- **default-isolation**: Same agent group if you want your agent to handle email alongside other channels. Separate agent group if email contains sensitive correspondence that shouldn't be accessible from other channels.\n\n## Troubleshooting\n\n**Sends fail with 401.** The API key comes from the Resend dashboard's **API Keys** page and starts with `re_`. Keys are shown once at creation — if in doubt, create a new one and update `RESEND_API_KEY` in `.env`.\n\n**The hello email never lands, or hits spam.** The sending domain must show as verified under Resend → **Domains** — until the SPF/DKIM DNS records propagate, sends are rejected or spam-foldered. `RESEND_FROM_ADDRESS` must be an address on that verified domain; a free-mail from-address will not work.\n\n**Replies never reach the agent.** Inbound only flows via the webhook: Resend → **Webhooks** must point at your public host at `/webhook/resend` (shared webhook server, port 3000) with the **email.received** event selected, and `RESEND_WEBHOOK_SECRET` must match that webhook's signing secret. Resend's webhook page lists delivery attempts — a run of failures means the URL is unreachable or the secret mismatches.\n\n**Adapter installed but nothing flows.** Run `pnpm exec vitest run src/channels/resend-registration.test.ts` — red means the barrel import or the `@resend/chat-sdk-adapter` install drifted, so re-run the Apply steps. If green, restart the service so it loads the adapter and `.env`, then re-send the hello.","author":"@nanocoai","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/nanocoai/nanoclaw/tree/main/.claude/skills/add-resend","license":"MIT","category":"writing","lang":"en","tokens":1847,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"apply-fixtures.json","size":536,"sha256":"15ca4ea479b07a0760170f8e453a0de75142e36b37e89742d80146fd62386521"},{"path":"REMOVE.md","size":790,"sha256":"7b75696837d4d3eb9b4c77ed20822555ea15ebae4554a2ff1ab65e23cac11310"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["resend.com"]}}