{
  "schemaVersion": "1.0",
  "item": {
    "slug": "email-resend",
    "name": "Email Resend",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/ivelin/email-resend",
    "canonicalUrl": "https://clawhub.ai/ivelin/email-resend",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/email-resend",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=email-resend",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "README.md",
      "SKILL.md",
      "cron-prompts/email-inbound.md",
      "docs/custody-chain.md",
      "scripts/configure-cron.py",
      "scripts/download_attachment.py"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/email-resend"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/email-resend",
    "agentPageUrl": "https://openagent3.xyz/skills/email-resend/agent",
    "manifestUrl": "https://openagent3.xyz/skills/email-resend/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/email-resend/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "License",
        "body": "Apache License 2.0 — See LICENSE file for full text."
      },
      {
        "title": "Email via Resend",
        "body": "Send and receive emails using the Resend API."
      },
      {
        "title": "Configuration",
        "body": "No config file needed. The skill auto-discovers settings from:\n\nEnvironment variables — RESEND_API_KEY (required), DEFAULT_FROM_EMAIL/NAME (optional)\nPreferences file — memory/email-preferences.md (from_email, from_name, telegram target)\nOpenClaw context — channel, chat_id, thread_id (for cron delivery)"
      },
      {
        "title": "Required Environment Variables",
        "body": "export RESEND_API_KEY=\"re_123456789\"        # Resend API key (required)\n# DEFAULT_FROM_EMAIL and DEFAULT_FROM_NAME are optional - loaded from preferences file if not set"
      },
      {
        "title": "Preferences File",
        "body": "The skill reads sender info from memory/email-preferences.md:\n\n---\nfrom_email: you@company.com\nfrom_name: Your Name\ntelegram:\n  target: \"CHAT_ID\"\n  threadId: \"THREAD_ID\"\n---\n\nScripts check env vars first, then fall back to preferences file."
      },
      {
        "title": "First-Time Setup",
        "body": "When the skill is first invoked, the sub-agent should:\n\nCheck context — OpenClaw context already has:\n\ncontext.user.email (from USER.md)\ncontext.channel (from current session)\ncontext.chat_id\ncontext.thread_id (for topics)\n\n\n\nCheck memory — Use memory_get tool:\n\nTry: memory_get path=\"memory/email-preferences.md\"\nIf not found, ask user to create memory/email-preferences.md (NO fallback scanning)\n\n\n\nIf missing, ask user — Via chat message (IMPORTANT for cron jobs):\n\n\"Which email should I send from?\" (from_email)\n\"What's your display name for sent emails?\" (from_name)\n\"Which channel/topic should I notify you on?\" (telegram target + threadId)\n\nThen create memory/email-preferences.md with their answers using the format above.\n\n\nCommit to memory — Write preferences to persist across sessions:\nwrite path=\"memory/email-preferences.md\" content=\"---\nfrom_email: $EMAIL\nfrom_name: $NAME\ntelegram:\n  target: \\\"$CHAT_ID\\\"\n  threadId: \\\"$THREAD_ID\\\"\n---\n\n# Email Notification Preferences\nSaved auto-configured\n\"\n\nThis ensures memory_get finds it in future sessions. Use MD format with YAML frontmatter.\n\nFormat (MD with YAML frontmatter):\n\n---\nfrom_email: you@company.com\nfrom_name: Your Name\ntelegram:\n  target: \\\"123456789\\\"\n  threadId: \\\"334\\\"\n---\n\n# Email Notification Preferences\n- **Updated:** 2026-01-01\n- **Purpose:** Default notification channel for email alerts\n\nImportant: Store in memory/email-preferences.md (NOT MEMORY.md) - isolated cron jobs can read this file via memory_get but NOT MEMORY.md."
      },
      {
        "title": "Context Fields (Available in Sub-Agent)",
        "body": "FieldSourceExampleuser.emailUSER.mdyou@company.comuser.nameUSER.mdYour NamechannelOpenClawfrom contextchat_idOpenClaw123456789thread_idOpenClaw334\n\nThe skill uses these directly from OpenClaw context — no parsing needed."
      },
      {
        "title": "Cron Setup",
        "body": "There are two ways to configure the cron:\n\nOption 1: Static (Hardcoded Target)\n\nUse this if you always want the same delivery target:\n\nopenclaw cron add \\\n  --name \"email-resend-inbound\" \\\n  --cron \"*/15 * * * *\" \\\n  --message \"Follow instructions in skills/email-resend/cron-prompts/email-inbound.md exactly. If new emails found, include them in your reply.\" \\\n  --session isolated \\\n  --announce \\\n  --channel telegram \\\n  --to \"-1003748898773:topic:334\"\n\nOption 2: Dynamic (From Preferences) — Recommended\n\nThis reads your notification preferences from memory/email-preferences.md and configures the cron automatically.\n\nRun:\n\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/configure-cron.py\n\nWhat it does:\n\nReads memory/email-preferences.md for your telegram target/threadId\nDeletes any existing email-resend-inbound cron\nCreates a new cron with your preferred delivery target\n\nFirst-time setup: If preferences don't exist, it will tell you what to configure.\n\nParameters:\n\n--schedule \"cron */15 * * * *\" — Run every 15 minutes\n--session isolated — Required for agentTurn payloads\n--announce — Enable delivery of results to chat\n--channel telegram — Delivery channel\n--to — Telegram target (format: chat_id:topic:thread_id)\n\nNote: The cron prompt reads notification preferences from memory/email-preferences.md. On first run, if preferences are missing, it will ask you for:\n\nWhich channel for notifications (telegram, discord, etc.)\nChat ID and Thread ID (for topics)"
      },
      {
        "title": "Manual Check",
        "body": "python3 ~/.openclaw/workspace/skills/email-resend/scripts/inbound.py"
      },
      {
        "title": "Notification Format",
        "body": "Each new email triggers a notification with:\n\nFrom, Subject, Date\nBody preview (~2000 chars)\nAttachment list (if any)\nImportance: 🔥 HIGH / 📅 MEETING / 📬 NORMAL"
      },
      {
        "title": "Acknowledge Flow (CRITICAL)",
        "body": "NEVER auto-acknowledge emails. Only the user can acknowledge by:\n\nReplying to the notification message, OR\nTyping: done / ack\n\nEmails must remain in pending state until user explicitly acknowledges.\n\nUse draft-reply.py to compose replies with proper quoting.\n\nImportant: Always use inline replies ([[reply_to_current]]) to keep messages linked in the thread. This enables:\n\nProper custody chain tracking\nReply-to-email tracing\nBetter conversation flow\n\nCRITICAL: When responding via OpenClaw message tool, use replyTo parameter (not [[reply_to_current]] tag):\n\nmessage(action=\"send\", channel=\"<from-context>\", replyTo=\"<msg_id>\", ...)"
      },
      {
        "title": "Scripts",
        "body": "ScriptPurposeinbound.pyCheck emails, send notificationsdraft-reply.pyDraft reply workflow with quoting & threadingoutbound.pySend emails directlydownload_attachment.pyDownload attachments from inbound emails"
      },
      {
        "title": "Downloading Attachments",
        "body": "To download attachments from an inbound email:\n\n# List attachments (shows IDs)\npython3 scripts/download_attachment.py <email_id> --list\n\n# Download all to directory\npython3 scripts/download_attachment.py <email_id> --output-dir ./attachments\n\n# Download specific attachment\npython3 scripts/download_attachment.py <email_id> --attachment-id <attachment_id>\n\nNote: The API path is /emails/receiving/{email_id}/attachments (not the standard /emails/ path)."
      },
      {
        "title": "State Files",
        "body": "memory/email-resend-inbound-notified.json — pending/acknowledged emails\nmemory/email-message-map.json — notification message_id → email_id (legacy)\nmemory/email-custody-chain.json — Full DAG of email → notification → actions\nmemory/email-msg-to-chain.json — notification message_id → chain lookup\nmemory/email-draft-state.json — Active draft state (email_id, status, reply_content)\n\nSee docs/custody-chain.md for DAG design."
      },
      {
        "title": "Outbound (Send)",
        "body": "python3 ~/.openclaw/workspace/skills/email-resend/scripts/outbound.py \\\n  --to \"recipient@example.com\" \\\n  --subject \"Hello\" \\\n  --body \"Message text\"\n\n# With attachments\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/outbound.py \\\n  --to \"recipient@example.com\" \\\n  --subject \"Here's the file\" \\\n  --body \"See attachment\" \\\n  --attachment ./file.pdf \\\n  --attachment ./image.png"
      },
      {
        "title": "⚠️ CRITICAL: Email Threading Rule (2026-02-22)",
        "body": "MANDATORY: Always use draft-reply.py for replying to emails.\n\nThis is non-negotiable. Failure to follow this rule will result in broken Gmail threading."
      },
      {
        "title": "Why This Matters",
        "body": "Gmail threads emails based on In-Reply-To AND References headers\nUsing wrong headers = reply appears as NEW thread = context lost\nThere's no way to fix this after sending"
      },
      {
        "title": "✅ Correct Workflow (ALWAYS USE THIS)",
        "body": "# Step 1: Start draft (fetches Message-ID automatically)\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/draft-reply.py start <email_id>\n\n# Step 2: Set reply content\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/draft-reply.py content \"Your reply\"\n\n# Step 3: Send\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/draft-reply.py send"
      },
      {
        "title": "⚠️ CRITICAL: Approval Execution Rule (2026-02-22)",
        "body": "When user approves a draft, you MUST execute the send command immediately.\n\nThe mistake to avoid:\n\n❌ Show draft for approval → User says \"send\" → Only acknowledge, don't execute\n✅ Show draft for approval → User says \"send\" → RUN draft-reply.py send → Then confirm\n\nCorrect workflow:\n\n1. Show draft for approval\n2. User replies \"approve\", \"send\", \"yes\", or \"ok\"\n3. IMMEDIATELY run: draft-reply.py send\n4. Only THEN confirm to user\n\nNever:\n\nOnly acknowledge the approval without executing\nAsk for confirmation after user already approved\nWait to send - do it immediately"
      },
      {
        "title": "❌ NEVER Do These Things",
        "body": "NEVER use outbound.py for replies:\n\n# WRONG - will break threading\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/outbound.py \\\n  --to \"x@y.com\" --subject \"Re: Original\" --body \"Reply\"\n\nNEVER manually construct --reply-to flags:\n\n# WRONG - guessing Message-ID format never works\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/outbound.py \\\n  --to \"x@y.com\" --subject \"Re: Original\" --body \"Reply\" \\\n  --reply-to \"<some-guess>@resend\"\n\nNEVER skip the workflow when subject starts with \"Re:\":\n\n# WRONG - replying without threading headers breaks thread\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/outbound.py \\\n  --to \"x@y.com\" --subject \"Re: Previous Thread\" --body \"Quick reply\""
      },
      {
        "title": "outbound.py Only For New Emails",
        "body": "outbound.py is for new emails only (not replies):\n\nFirst contact\nAnnouncements\nEmails where you intentionally want a NEW thread\n\nFor anything that could be a reply, use draft-reply.py."
      },
      {
        "title": "Requirements",
        "body": "RESEND_API_KEY environment variable set\nPython requests library"
      },
      {
        "title": "Draft Reply Best Practices",
        "body": "When composing a reply via draft-reply.py:\n\nAlways quote the original — Include the original message with > prefix so recipient knows what you're responding to\n\n\nUse proper threading — Set In-Reply-To and References headers using the original email's Message-ID\n\n\nKeep subject line — Start with Re:  prefix to maintain thread (but avoid \"Re: Re:\")\n\n\nStructure:\nYour reply here\n\n---\n\nOn [date] [original sender] wrote:\n> quoted original message\n> continues here\n\n\n\nMultiple replies supported — After sending, draft is marked as \"sent\" so you can reply again to the same thread. Use resume command to continue.\n\n\nNo double Re: — If original subject already starts with \"Re:\", don't add another\n\n\nCustody Chain — Track full lineage:\n\nEmail → notification → All replies/actions\nDAG structure with parent links\nAny message traces back to original email"
      },
      {
        "title": "Draft Reply Commands",
        "body": "CommandPurposestart <email_id>Start a draft reply to an emailresumeContinue a sent thread to reply againcontent \"text\"Set reply contentsendSend the replycancelCancel the draftstatusShow current draft status\n\nAfter sending, use resume to reply again to the same thread — threading headers are preserved.\n\nRun tests:\n\npython3 skills/email-resend/tests/test_inbound.py\n\nExpected: 43+ tests total (test_inbound.py: 37, test_threading.py: 6, test_attachments.py: varies).\n\nIf tests fail:\n\nCheck which test failed and why\nFix the feature/code to match expected behavior\nOr update tests if feature intentionally changed"
      },
      {
        "title": "Required Credentials",
        "body": "RESEND_API_KEY — Required. Get from https://resend.com API settings. Create with minimal permissions.\nDEFAULT_FROM_EMAIL / DEFAULT_FROM_NAME — Optional. If not set, loaded from memory/email-preferences.md."
      },
      {
        "title": "Memory File Access",
        "body": "The skill reads ONLY from explicit preferences file:\n\nmemory/email-preferences.md — Required for telegram target/threadId\nNo fallback scanning of MEMORY.md, USER.md, TOOLS.md, or memory/*.md\n\nThis restricted approach prevents information leakage from sensitive files."
      },
      {
        "title": "Cron Job",
        "body": "The configure-cron.py script will create/delete a cron job named email-resend-inbound via OpenClaw CLI."
      },
      {
        "title": "Recommendations",
        "body": "Run tests with a dummy RESEND_API_KEY before enabling in production\nIf you only need outbound email, don't enable inbound/cron\nAudit memory/email-preferences.md to ensure it contains only necessary fields\nKeep preferences file minimal - only include required fields (target, threadId)"
      }
    ],
    "body": "License\n\nApache License 2.0 — See LICENSE file for full text.\n\nEmail via Resend\n\nSend and receive emails using the Resend API.\n\nConfiguration\n\nNo config file needed. The skill auto-discovers settings from:\n\nEnvironment variables — RESEND_API_KEY (required), DEFAULT_FROM_EMAIL/NAME (optional)\nPreferences file — memory/email-preferences.md (from_email, from_name, telegram target)\nOpenClaw context — channel, chat_id, thread_id (for cron delivery)\nRequired Environment Variables\nexport RESEND_API_KEY=\"re_123456789\"        # Resend API key (required)\n# DEFAULT_FROM_EMAIL and DEFAULT_FROM_NAME are optional - loaded from preferences file if not set\n\nPreferences File\n\nThe skill reads sender info from memory/email-preferences.md:\n\n---\nfrom_email: you@company.com\nfrom_name: Your Name\ntelegram:\n  target: \"CHAT_ID\"\n  threadId: \"THREAD_ID\"\n---\n\n\nScripts check env vars first, then fall back to preferences file.\n\nFirst-Time Setup\n\nWhen the skill is first invoked, the sub-agent should:\n\nCheck context — OpenClaw context already has:\n\ncontext.user.email (from USER.md)\ncontext.channel (from current session)\ncontext.chat_id\ncontext.thread_id (for topics)\n\nCheck memory — Use memory_get tool:\n\nTry: memory_get path=\"memory/email-preferences.md\"\nIf not found, ask user to create memory/email-preferences.md (NO fallback scanning)\n\nIf missing, ask user — Via chat message (IMPORTANT for cron jobs):\n\n\"Which email should I send from?\" (from_email)\n\"What's your display name for sent emails?\" (from_name)\n\"Which channel/topic should I notify you on?\" (telegram target + threadId)\n\nThen create memory/email-preferences.md with their answers using the format above.\n\nCommit to memory — Write preferences to persist across sessions:\n\nwrite path=\"memory/email-preferences.md\" content=\"---\nfrom_email: $EMAIL\nfrom_name: $NAME\ntelegram:\n  target: \\\"$CHAT_ID\\\"\n  threadId: \\\"$THREAD_ID\\\"\n---\n\n# Email Notification Preferences\nSaved auto-configured\n\"\n\n\nThis ensures memory_get finds it in future sessions. Use MD format with YAML frontmatter.\n\nFormat (MD with YAML frontmatter):\n\n---\nfrom_email: you@company.com\nfrom_name: Your Name\ntelegram:\n  target: \\\"123456789\\\"\n  threadId: \\\"334\\\"\n---\n\n# Email Notification Preferences\n- **Updated:** 2026-01-01\n- **Purpose:** Default notification channel for email alerts\n\n\nImportant: Store in memory/email-preferences.md (NOT MEMORY.md) - isolated cron jobs can read this file via memory_get but NOT MEMORY.md.\n\nContext Fields (Available in Sub-Agent)\nField\tSource\tExample\nuser.email\tUSER.md\tyou@company.com\nuser.name\tUSER.md\tYour Name\nchannel\tOpenClaw\tfrom context\nchat_id\tOpenClaw\t123456789\nthread_id\tOpenClaw\t334\n\nThe skill uses these directly from OpenClaw context — no parsing needed.\n\nUsage\nInbound (Receive)\nCron Setup\n\nThere are two ways to configure the cron:\n\nOption 1: Static (Hardcoded Target)\n\nUse this if you always want the same delivery target:\n\nopenclaw cron add \\\n  --name \"email-resend-inbound\" \\\n  --cron \"*/15 * * * *\" \\\n  --message \"Follow instructions in skills/email-resend/cron-prompts/email-inbound.md exactly. If new emails found, include them in your reply.\" \\\n  --session isolated \\\n  --announce \\\n  --channel telegram \\\n  --to \"-1003748898773:topic:334\"\n\nOption 2: Dynamic (From Preferences) — Recommended\n\nThis reads your notification preferences from memory/email-preferences.md and configures the cron automatically.\n\nRun:\n\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/configure-cron.py\n\n\nWhat it does:\n\nReads memory/email-preferences.md for your telegram target/threadId\nDeletes any existing email-resend-inbound cron\nCreates a new cron with your preferred delivery target\n\nFirst-time setup: If preferences don't exist, it will tell you what to configure.\n\nParameters:\n\n--schedule \"cron */15 * * * *\" — Run every 15 minutes\n--session isolated — Required for agentTurn payloads\n--announce — Enable delivery of results to chat\n--channel telegram — Delivery channel\n--to — Telegram target (format: chat_id:topic:thread_id)\n\nNote: The cron prompt reads notification preferences from memory/email-preferences.md. On first run, if preferences are missing, it will ask you for:\n\nWhich channel for notifications (telegram, discord, etc.)\nChat ID and Thread ID (for topics)\nManual Check\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/inbound.py\n\nNotification Format\n\nEach new email triggers a notification with:\n\nFrom, Subject, Date\nBody preview (~2000 chars)\nAttachment list (if any)\nImportance: 🔥 HIGH / 📅 MEETING / 📬 NORMAL\nAcknowledge Flow (CRITICAL)\n\nNEVER auto-acknowledge emails. Only the user can acknowledge by:\n\nReplying to the notification message, OR\nTyping: done / ack\n\nEmails must remain in pending state until user explicitly acknowledges.\n\nUse draft-reply.py to compose replies with proper quoting.\n\nImportant: Always use inline replies ([[reply_to_current]]) to keep messages linked in the thread. This enables:\n\nProper custody chain tracking\nReply-to-email tracing\nBetter conversation flow\n\nCRITICAL: When responding via OpenClaw message tool, use replyTo parameter (not [[reply_to_current]] tag):\n\nmessage(action=\"send\", channel=\"<from-context>\", replyTo=\"<msg_id>\", ...)\n\nScripts\nScript\tPurpose\ninbound.py\tCheck emails, send notifications\ndraft-reply.py\tDraft reply workflow with quoting & threading\noutbound.py\tSend emails directly\ndownload_attachment.py\tDownload attachments from inbound emails\nDownloading Attachments\n\nTo download attachments from an inbound email:\n\n# List attachments (shows IDs)\npython3 scripts/download_attachment.py <email_id> --list\n\n# Download all to directory\npython3 scripts/download_attachment.py <email_id> --output-dir ./attachments\n\n# Download specific attachment\npython3 scripts/download_attachment.py <email_id> --attachment-id <attachment_id>\n\n\nNote: The API path is /emails/receiving/{email_id}/attachments (not the standard /emails/ path).\n\nState Files\nmemory/email-resend-inbound-notified.json — pending/acknowledged emails\nmemory/email-message-map.json — notification message_id → email_id (legacy)\nmemory/email-custody-chain.json — Full DAG of email → notification → actions\nmemory/email-msg-to-chain.json — notification message_id → chain lookup\nmemory/email-draft-state.json — Active draft state (email_id, status, reply_content)\n\nSee docs/custody-chain.md for DAG design.\n\nOutbound (Send)\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/outbound.py \\\n  --to \"recipient@example.com\" \\\n  --subject \"Hello\" \\\n  --body \"Message text\"\n\n# With attachments\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/outbound.py \\\n  --to \"recipient@example.com\" \\\n  --subject \"Here's the file\" \\\n  --body \"See attachment\" \\\n  --attachment ./file.pdf \\\n  --attachment ./image.png\n\n⚠️ CRITICAL: Email Threading Rule (2026-02-22)\n\nMANDATORY: Always use draft-reply.py for replying to emails.\n\nThis is non-negotiable. Failure to follow this rule will result in broken Gmail threading.\n\nWhy This Matters\nGmail threads emails based on In-Reply-To AND References headers\nUsing wrong headers = reply appears as NEW thread = context lost\nThere's no way to fix this after sending\n✅ Correct Workflow (ALWAYS USE THIS)\n# Step 1: Start draft (fetches Message-ID automatically)\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/draft-reply.py start <email_id>\n\n# Step 2: Set reply content\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/draft-reply.py content \"Your reply\"\n\n# Step 3: Send\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/draft-reply.py send\n\n⚠️ CRITICAL: Approval Execution Rule (2026-02-22)\n\nWhen user approves a draft, you MUST execute the send command immediately.\n\nThe mistake to avoid:\n\n❌ Show draft for approval → User says \"send\" → Only acknowledge, don't execute\n✅ Show draft for approval → User says \"send\" → RUN draft-reply.py send → Then confirm\n\nCorrect workflow:\n\n1. Show draft for approval\n2. User replies \"approve\", \"send\", \"yes\", or \"ok\"\n3. IMMEDIATELY run: draft-reply.py send\n4. Only THEN confirm to user\n\n\nNever:\n\nOnly acknowledge the approval without executing\nAsk for confirmation after user already approved\nWait to send - do it immediately\n❌ NEVER Do These Things\n\nNEVER use outbound.py for replies:\n\n# WRONG - will break threading\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/outbound.py \\\n  --to \"x@y.com\" --subject \"Re: Original\" --body \"Reply\"\n\n\nNEVER manually construct --reply-to flags:\n\n# WRONG - guessing Message-ID format never works\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/outbound.py \\\n  --to \"x@y.com\" --subject \"Re: Original\" --body \"Reply\" \\\n  --reply-to \"<some-guess>@resend\"\n\n\nNEVER skip the workflow when subject starts with \"Re:\":\n\n# WRONG - replying without threading headers breaks thread\npython3 ~/.openclaw/workspace/skills/email-resend/scripts/outbound.py \\\n  --to \"x@y.com\" --subject \"Re: Previous Thread\" --body \"Quick reply\"\n\noutbound.py Only For New Emails\n\noutbound.py is for new emails only (not replies):\n\nFirst contact\nAnnouncements\nEmails where you intentionally want a NEW thread\n\nFor anything that could be a reply, use draft-reply.py.\n\nRequirements\nRESEND_API_KEY environment variable set\nPython requests library\nDraft Reply Best Practices\n\nWhen composing a reply via draft-reply.py:\n\nAlways quote the original — Include the original message with > prefix so recipient knows what you're responding to\n\nUse proper threading — Set In-Reply-To and References headers using the original email's Message-ID\n\nKeep subject line — Start with Re: prefix to maintain thread (but avoid \"Re: Re:\")\n\nStructure:\n\nYour reply here\n\n---\n\nOn [date] [original sender] wrote:\n> quoted original message\n> continues here\n\n\nMultiple replies supported — After sending, draft is marked as \"sent\" so you can reply again to the same thread. Use resume command to continue.\n\nNo double Re: — If original subject already starts with \"Re:\", don't add another\n\nCustody Chain — Track full lineage:\n\nEmail → notification → All replies/actions\nDAG structure with parent links\nAny message traces back to original email\nDraft Reply Commands\nCommand\tPurpose\nstart <email_id>\tStart a draft reply to an email\nresume\tContinue a sent thread to reply again\ncontent \"text\"\tSet reply content\nsend\tSend the reply\ncancel\tCancel the draft\nstatus\tShow current draft status\n\nAfter sending, use resume to reply again to the same thread — threading headers are preserved.\n\nRun tests:\n\npython3 skills/email-resend/tests/test_inbound.py\n\n\nExpected: 43+ tests total (test_inbound.py: 37, test_threading.py: 6, test_attachments.py: varies).\n\nIf tests fail:\n\nCheck which test failed and why\nFix the feature/code to match expected behavior\nOr update tests if feature intentionally changed\n⚠️ Privacy & Security Considerations\nRequired Credentials\nRESEND_API_KEY — Required. Get from https://resend.com API settings. Create with minimal permissions.\nDEFAULT_FROM_EMAIL / DEFAULT_FROM_NAME — Optional. If not set, loaded from memory/email-preferences.md.\nMemory File Access\n\nThe skill reads ONLY from explicit preferences file:\n\nmemory/email-preferences.md — Required for telegram target/threadId\nNo fallback scanning of MEMORY.md, USER.md, TOOLS.md, or memory/*.md\n\nThis restricted approach prevents information leakage from sensitive files.\n\nCron Job\n\nThe configure-cron.py script will create/delete a cron job named email-resend-inbound via OpenClaw CLI.\n\nRecommendations\nRun tests with a dummy RESEND_API_KEY before enabling in production\nIf you only need outbound email, don't enable inbound/cron\nAudit memory/email-preferences.md to ensure it contains only necessary fields\nKeep preferences file minimal - only include required fields (target, threadId)"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/ivelin/email-resend",
    "publisherUrl": "https://clawhub.ai/ivelin/email-resend",
    "owner": "ivelin",
    "version": "1.0.14",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/email-resend",
    "downloadUrl": "https://openagent3.xyz/downloads/email-resend",
    "agentUrl": "https://openagent3.xyz/skills/email-resend/agent",
    "manifestUrl": "https://openagent3.xyz/skills/email-resend/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/email-resend/agent.md"
  }
}