### Manual Setup: Clone, Install, and Configure Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Steps to clone the Clawpost repository, install dependencies, and set up initial configuration files. ```bash git clone https://github.com/hirefrank/clawpost.git && cd clawpost bun install ``` -------------------------------- ### Manual Setup: Configure Environment Variables Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Instructions for configuring wrangler.toml and copying example environment variables for local development. ```bash # Edit wrangler.toml — paste database_id, set FROM_EMAIL, FROM_NAME cp .dev.vars.example .dev.vars # set CLAWPOST_API_KEY (+ RESEND_API_KEY if using Resend) ``` -------------------------------- ### Manual Setup: Deploy Clawpost Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Command to deploy the Clawpost application. ```bash bun run deploy ``` -------------------------------- ### Manual Setup: Create Cloudflare Resources Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Commands to create the necessary Cloudflare D1 database and R2 bucket for Clawpost. ```bash wrangler d1 create clawpost-db # note the database_id in the output wrangler r2 bucket create clawpost-attachments ``` -------------------------------- ### Manual Setup: Apply D1 Migrations Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Command to apply database migrations for the D1 database. ```bash bun run db:migrate ``` -------------------------------- ### Manual Setup: Set Optional Webhook Secrets Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Commands to set optional webhook secrets for outbound and Resend delivery webhooks. ```bash wrangler secret put WEBHOOK_SECRET # HMAC key for outbound webhooks wrangler secret put RESEND_WEBHOOK_SECRET # token for Resend delivery webhooks ``` -------------------------------- ### Manual Setup: Set Production Secrets Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Commands to set essential production secrets for Clawpost using Wrangler. ```bash wrangler secret put CLAWPOST_API_KEY # wrangler secret put RESEND_API_KEY # only if using Resend wrangler secret put CLAWPOST_CF_API_TOKEN # optional: enable CLI/API inbox + alias management wrangler secret put CLAWPOST_CF_ACCOUNT_ID # optional: account that owns destination addresses ``` -------------------------------- ### Deploy Worker and Run Migrations Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Steps to deploy the Clawpost worker, set API keys, and configure optional settings for inbox and alias management. Includes commands for installation, migration, deployment, and secret management. ```bash bun install bun run db:migrate bun run deploy wrangler secret put CLAWPOST_API_KEY wrangler secret put CLAWPOST_CF_API_TOKEN # optional, needed for inbox/alias management wrangler secret put CLAWPOST_CF_ACCOUNT_ID # optional, needed for inbox/alias management # set CLAWPOST_ALLOWED_DOMAINS in wrangler.toml if you want to restrict routing changes export CLAWPOST_BASE_URL="https://.workers.dev" export CLAWPOST_API_KEY="..." bun run build:cli node dist/clawpost.js routing discover --domain hirefrank.com node dist/clawpost.js inbox create quinn@hirefrank.com node dist/clawpost.js alias create sh@hirefrank.com --to fcharris@gmail.com --to nellwyn.thomas@gmail.com ``` -------------------------------- ### Import Legacy Inbox Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Imports a legacy inbox configuration. This is useful for migrating existing email setups. ```bash node dist/clawpost.js inbox import legacy@hirefrank.com ``` -------------------------------- ### Build and Use Clawpost CLI Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Instructions for building the Clawpost CLI and using it to manage messages and aliases. Requires setting the base URL and API key environment variables. ```bash export CLAWPOST_BASE_URL="https://.workers.dev" export CLAWPOST_API_KEY="..." bun run build:cli node dist/clawpost.js message list node dist/clawpost.js alias create sh@hirefrank.com --to fcharris@gmail.com --to nellwyn.thomas@gmail.com ``` -------------------------------- ### Development and Deployment Commands Source: https://github.com/kritsanan1/clawpost/blob/main/AGENTS.md Common commands for running the development server, deploying the application, managing database migrations, type checking, and building the CLI. ```bash bun run dev # wrangler dev --remote (requires wrangler.toml with real IDs) bun run deploy # wrangler deploy bun run db:migrate # wrangler d1 migrations apply DB --remote bun run typecheck # tsc --noEmit (no tests yet) bun run cli # run CLI from source: bun run ./src/cli/index.ts bun run build:cli # bundle CLI to dist/clawpost.js for Node ``` -------------------------------- ### CLI Bootstrap and Message Listing Source: https://github.com/kritsanan1/clawpost/blob/main/SKILL.md Set environment variables for Clawpost API access and list the latest messages. ```bash export CLAWPOST_BASE_URL="https://.workers.dev" export CLAWPOST_API_KEY="..." clawpost --json message list --limit 10 ``` -------------------------------- ### Create Alias Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Creates a new alias. Requires `X-API-Key` header. ```APIDOC ## POST /api/aliases ### Description Create alias ### Method POST ### Endpoint /api/aliases ### Request Body - **source** (string) - Required - The source email address for the alias. - **destinations** (array) - Required - An array of destination email addresses. - **enabled** (boolean) - Optional - Whether the alias is enabled. ``` -------------------------------- ### Configure Claude Desktop for Clawpost Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Configuration for Claude Desktop to use the Streamable HTTP transport with Clawpost. Requires the worker URL and Bearer token authentication. ```json { "mcpServers": { "clawpost": { "type": "streamable-http", "url": "https://.workers.dev/mcp", "headers": { "Authorization": "Bearer YOUR_CLAWPOST_API_KEY" } } } } ``` -------------------------------- ### Cloudflare Routing System - Import Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Details the functionality for importing existing Cloudflare routing rules into the Clawpost database. ```typescript // Import: importInbox()/importAlias() adopt existing Cloudflare rules into Clawpost DB ``` -------------------------------- ### Create Draft Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Creates a new draft email. Requires `X-API-Key` header. ```APIDOC ## POST /api/drafts ### Description Create draft ### Method POST ### Endpoint /api/drafts ### Request Body - **to** (string) - Optional - Recipient email address. - **cc** (string) - Optional - CC recipient email address. - **bcc** (string) - Optional - BCC recipient email address. - **subject** (string) - Optional - Subject of the draft. - **body_text** (string) - Optional - Plain text content of the draft. - **thread_id** (string) - Optional - The ID of the thread this draft belongs to. ``` -------------------------------- ### Discover Routing Information Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Use this command to discover routing information for a given domain. ```bash node dist/clawpost.js routing discover --domain hirefrank.com ``` -------------------------------- ### Import Forwarding Alias Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Imports an existing forwarding alias configuration. Ensure the destination addresses are validated in Cloudflare. ```bash node dist/clawpost.js alias import founders@hirefrank.com --to fcharris@gmail.com --to nellwyn.thomas@gmail.com ``` -------------------------------- ### Import Alias Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Imports an alias from an existing worker route. Requires `X-API-Key` header. ```APIDOC ## POST /api/aliases/import ### Description Import alias from existing worker route ### Method POST ### Endpoint /api/aliases/import ### Request Body - **source** (string) - Required - The source email address for the alias. - **destinations** (array) - Required - An array of destination email addresses. ``` -------------------------------- ### Database Schema and Client Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Specifies the location of database schema types and the client factory, recommending the use of Kysely's sql tag for raw SQL expressions. ```typescript // Database: Kysely over D1 via kysely-d1. Schema types in src/db/schema.ts, factory in src/db/client.ts. Use sql template tag from Kysely for raw expressions (e.g., sql`message_count + 1`), not db.raw(). ``` -------------------------------- ### Import Alias Source: https://github.com/kritsanan1/clawpost/blob/main/docs/domain-and-routing-gaps.md This CLI command facilitates the import of an existing alias. It focuses on adopting exact-address Cloudflare rules that are already configured to target the worker. ```bash wrangler alias import ``` -------------------------------- ### Create Inbox Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Creates a new inbox. Requires `X-API-Key` header. ```APIDOC ## POST /api/inboxes ### Description Create inbox ### Method POST ### Endpoint /api/inboxes ### Request Body - **email** (string) - Required - The email address for the new inbox. - **enabled** (boolean) - Optional - Whether the inbox is enabled. ``` -------------------------------- ### Create Inbox Source: https://github.com/kritsanan1/clawpost/blob/main/SKILL.md Create a new managed inbox address. ```bash clawpost inbox create quinn@hirefrank.com ``` -------------------------------- ### Email Provider Selection Logic Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Explains how the email provider is selected based on environment variables and the fallback mechanism. ```typescript // Email provider selection: set EMAIL_PROVIDER var to "cloudflare" or "resend", or omit to auto-detect (prefers Cloudflare EMAIL binding, falls back to RESEND_API_KEY) ``` -------------------------------- ### Create Alias Source: https://github.com/kritsanan1/clawpost/blob/main/SKILL.md Create a new forwarding alias with one or more destinations. ```bash clawpost alias create sh@hirefrank.com --to fcharris@gmail.com --to nellwyn.thomas@gmail.com ``` -------------------------------- ### Import Inbox Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Imports an inbox from an existing worker route. Requires `X-API-Key` header. ```APIDOC ## POST /api/inboxes/import ### Description Import inbox from existing worker route ### Method POST ### Endpoint /api/inboxes/import ### Request Body - **email** (string) - Required - The email address of the inbox to import. ``` -------------------------------- ### Configure Mcporter for Clawpost Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Configuration for the mcporter client to connect to the Clawpost MCP server. Requires setting the CLAWPOST_API_KEY environment variable or embedding it directly. ```json { "mcpServers": { "clawpost": { "description": "Email for AI agents — send, receive, search, and manage threads", "baseUrl": "https://.workers.dev/mcp", "headers": { "Authorization": "Bearer ${CLAWPOST_API_KEY}" } } } } ``` -------------------------------- ### Cloudflare Routing System - Discovery Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Describes the process of discovering existing Cloudflare routing rules that target the Worker. ```typescript // Discovery: discoverRoutingRules() scans Cloudflare zones for existing rules targeting the Worker ``` -------------------------------- ### Standalone CLI Configuration Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Configuration details for the standalone CLI, including its communication method with the deployed Worker and input handling. ```typescript // Standalone CLI (src/cli/): HTTP-backed, repo-independent. Talks to the deployed Worker via CLAWPOST_BASE_URL and CLAWPOST_API_KEY. Commander.js with --json for machine output. Body input accepts --body, --body-file, or piped stdin. ``` -------------------------------- ### Configure Allowed Domains Source: https://github.com/kritsanan1/clawpost/blob/main/docs/domain-and-routing-gaps.md This configuration variable, `CLAWPOST_ALLOWED_DOMAINS`, is used to specify a comma-separated list of domains that Clawpost is permitted to use. It's validated during create/import operations and used as a default scope for discovery. ```text CLAWPOST_ALLOWED_DOMAINS=example.com,another.org ``` -------------------------------- ### Import Inbox Source: https://github.com/kritsanan1/clawpost/blob/main/docs/domain-and-routing-gaps.md This CLI command allows importing an existing inbox. It's designed to work with exact-address Cloudflare rules that already target the configured worker. ```bash wrangler inbox import ``` -------------------------------- ### Full-Text Search Implementation Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Describes the use of FTS5 for full-text search in the database and the fallback mechanism to LIKE queries. ```typescript // Full-text search: messages_fts FTS5 virtual table synced via SQLite triggers. Search endpoints try FTS5 MATCH first and fall back to LIKE on invalid query syntax. ``` -------------------------------- ### Cloudflare Routing System - Aliases Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Explains the management of Cloudflare Email Routing rules for aliases, including multi-destination support and rollback mechanisms. ```typescript // Aliases: CRUD with multi-destination support; fan-out via Worker EMAIL binding; destination rows and CF rule rolled back on failure ``` -------------------------------- ### List Threads Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves a list of message threads with optional pagination. Requires `X-API-Key` header. ```APIDOC ## GET /api/threads ### Description List threads ### Method GET ### Endpoint /api/threads ### Query Parameters - **limit** (integer) - Optional - Maximum number of threads to return. - **offset** (integer) - Optional - Number of threads to skip. ``` -------------------------------- ### Discover Existing Routing Rules Source: https://github.com/kritsanan1/clawpost/blob/main/docs/domain-and-routing-gaps.md This CLI command helps discover existing Cloudflare Email Routing rules that target the configured worker. It's part of the flow to adopt existing rules into Clawpost. ```bash wrangler routing discover ``` -------------------------------- ### List Managed Aliases Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves a list of managed aliases and their destinations. Requires `X-API-Key` header. ```APIDOC ## GET /api/aliases ### Description List managed aliases and destinations ### Method GET ### Endpoint /api/aliases ``` -------------------------------- ### List Drafts Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves a list of drafts with optional pagination. Requires `X-API-Key` header. ```APIDOC ## GET /api/drafts ### Description List drafts ### Method GET ### Endpoint /api/drafts ### Query Parameters - **limit** (integer) - Optional - Maximum number of drafts to return. - **offset** (integer) - Optional - Number of drafts to skip. ``` -------------------------------- ### MCP Server Tool Registration Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Illustrates how tools are registered within the McpAgent Durable Object using Zod schemas for validation. ```typescript // Tools registered in init() using this.server.registerTool() with Zod schemas. ``` -------------------------------- ### Create a Forwarding Alias with Multiple Destinations Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Creates a forwarding alias for an email address, directing mail to multiple specified destinations. This utilizes Cloudflare's multi-destination alias capabilities. ```bash node dist/clawpost.js alias create sh@hirefrank.com --to fcharris@gmail.com --to nellwyn.thomas@gmail.com ``` -------------------------------- ### Read Alias Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves details of a specific alias, including its destinations. Requires `X-API-Key` header. ```APIDOC ## GET /api/aliases/:id ### Description Read alias + destinations ### Method GET ### Endpoint /api/aliases/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the alias to retrieve. ``` -------------------------------- ### Query Email Flow Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Illustrates the flow for querying emails, using an MCP tool or API to access D1 with FTS5 for search results. ```text Query: MCP tool / API → D1 (FTS5) → results ``` -------------------------------- ### Create an Inbox Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Creates a new inbox for a specific email address. This command is part of the inbox management tools. ```bash node dist/clawpost.js inbox create quinn@hirefrank.com ``` -------------------------------- ### Cloudflare Routing System - Domain Guard Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Explains the domain validation mechanism to restrict operations to allowed domains. ```typescript // Domain guard: ALLOWED_DOMAINS env var (comma-separated) validated on create, import, and discovery. If unset, no restriction. ``` -------------------------------- ### Discover Worker Routes Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Discovers exact-address Cloudflare worker routes for a given domain. Requires `X-API-Key` header. ```APIDOC ## GET /api/routing/discover ### Description Discover exact-address Cloudflare worker routes ### Method GET ### Endpoint /api/routing/discover ### Query Parameters - **domain** (string) - Required - The domain to discover routes for. ``` -------------------------------- ### Read Draft Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves a specific draft email. Requires `X-API-Key` header. ```APIDOC ## GET /api/drafts/:id ### Description Read a draft ### Method GET ### Endpoint /api/drafts/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the draft to retrieve. ``` -------------------------------- ### Per-Provider Sender Configuration Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Specifies the required and optional configuration variables for sender details, including overrides for Resend. ```typescript // Per-provider sender config: FROM_EMAIL/FROM_NAME required; REPLY_TO_EMAIL optional. Set RESEND_FROM_EMAIL/RESEND_FROM_NAME/RESEND_REPLY_TO_EMAIL to override when Resend uses a different sending domain ``` -------------------------------- ### Create Inbox with Compensation Source: https://github.com/kritsanan1/clawpost/blob/main/docs/domain-and-routing-gaps.md This function handles the creation of an inbox. It now includes a best-effort compensation mechanism: if the database insert fails after the Cloudflare rule is created, the Cloudflare rule will be deleted. ```typescript async createInbox(args: { // ... other args domain: string; emailLocalPart: string; }) { const { zoneId, emailLocalPart, domain } = args; const pattern = `${emailLocalPart}@${domain}`; // Create the Cloudflare rule first const route = await createOrUpdateWorkerRoute({ zoneId, routeId: null, pattern, script: env.CLOUDFLARE_WORKER_NAME, enabled: true, custom_metadata: { clawpost_inbox_id: "" } // Placeholder }); try { // Insert the DB row const inbox = await db.insertInto('inboxes').values({ // ... values cf_rule_id: route.id, // ... }).returningAll().executeTakeFirstOrThrow(); return inbox; } catch (e) { // Compensation: Delete the Cloudflare rule if DB insert fails await deleteWorkerRoute(zoneId, route.id); throw e; } } ``` -------------------------------- ### Inbound Email Flow Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Illustrates the flow of an inbound email through the Clawpost system, from Cloudflare Email Routing to D1 and R2 storage. ```text Inbound: email → CF Email Routing → Worker → postal-mime → D1 + R2 → webhook ``` -------------------------------- ### Reply to Message Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Sends a reply to an existing approved message. Requires `X-API-Key` header. ```APIDOC ## POST /api/messages/:id/reply ### Description Reply to approved message ### Method POST ### Endpoint /api/messages/:id/reply ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the message to reply to. ### Request Body - **body** (string) - Required - The content of the reply. ``` -------------------------------- ### Cloudflare Routing System - Inboxes Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Details the management of Cloudflare Email Routing rules for inboxes, including CRUD operations and synchronization with the database. ```typescript // Inboxes: CRUD with Cloudflare rule sync and rollback safety on create/update/delete ``` -------------------------------- ### Read Thread Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves a specific thread, including all its approved messages. Requires `X-API-Key` header. ```APIDOC ## GET /api/threads/:id ### Description Thread with all approved messages ### Method GET ### Endpoint /api/threads/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the thread to retrieve. ``` -------------------------------- ### Send Email Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Sends an email with specified recipients, subject, body, and optional attachments. Requires `X-API-Key` header. ```APIDOC ## POST /api/send ### Description Send email (to, subject, body, cc, bcc, attachments) ### Method POST ### Endpoint /api/send ### Request Body - **to** (string) - Required - Recipient email address. - **subject** (string) - Required - Subject of the email. - **body** (string) - Required - HTML or plain text content of the email. - **cc** (array) - Optional - Array of CC recipient email addresses. - **bcc** (array) - Optional - Array of BCC recipient email addresses. - **attachments** (array) - Optional - Array of attachment objects. ``` -------------------------------- ### Unarchive Message Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Unarchives a message. Requires `X-API-Key` header. ```APIDOC ## POST /api/messages/:id/unarchive ### Description Unarchive a message ### Method POST ### Endpoint /api/messages/:id/unarchive ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the message to unarchive. ``` -------------------------------- ### Approve Sender Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Approves an email sender. Requires `X-API-Key` header. ```APIDOC ## POST /api/approved-senders ### Description Approve a sender ### Method POST ### Endpoint /api/approved-senders ### Request Body - **email** (string) - Required - The email address of the sender to approve. - **name** (string) - Optional - The name associated with the sender. ``` -------------------------------- ### Download Attachment Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Downloads an attachment associated with an approved message. Requires `X-API-Key` header. ```APIDOC ## GET /api/attachments/:id ### Description Download attachment (approved messages only) ### Method GET ### Endpoint /api/attachments/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the attachment to download. ``` -------------------------------- ### List Managed Inboxes Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves a list of managed inboxes. Requires `X-API-Key` header. ```APIDOC ## GET /api/inboxes ### Description List managed inboxes ### Method GET ### Endpoint /api/inboxes ``` -------------------------------- ### Add Labels to Message Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Adds one or more labels to an approved message. Requires `X-API-Key` header. ```APIDOC ## POST /api/messages/:id/labels ### Description Add labels to an approved message ### Method POST ### Endpoint /api/messages/:id/labels ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the message to add labels to. ### Request Body - **labels** (array) - Required - An array of labels to add. ``` -------------------------------- ### Send Draft Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Sends a draft email. The draft is deleted after successful sending. Requires `X-API-Key` header. ```APIDOC ## POST /api/drafts/:id/send ### Description Send a draft (deletes after) ### Method POST ### Endpoint /api/drafts/:id/send ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the draft to send. ``` -------------------------------- ### List Approved Senders Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves a list of all approved senders. Requires `X-API-Key` header. ```APIDOC ## GET /api/approved-senders ### Description List approved senders ### Method GET ### Endpoint /api/approved-senders ``` -------------------------------- ### Archive Message Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Archives an approved message. Requires `X-API-Key` header. ```APIDOC ## POST /api/messages/:id/archive ### Description Archive a message ### Method POST ### Endpoint /api/messages/:id/archive ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the message to archive. ``` -------------------------------- ### Update Alias Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Updates an existing alias. Requires `X-API-Key` header. ```APIDOC ## PUT /api/aliases/:id ### Description Update alias ### Method PUT ### Endpoint /api/aliases/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the alias to update. ### Request Body - **source** (string) - Optional - The source email address for the alias. - **destinations** (array) - Optional - An array of destination email addresses. - **enabled** (boolean) - Optional - Whether the alias is enabled. ``` -------------------------------- ### Outbound Email Sending Logic Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Details the process for sending outbound emails, including provider selection, sender configuration, and database status updates. ```typescript // Outbound email (src/mail.ts): sends via Cloudflare Email Service (default) or Resend → stores in D1 with approved=1 and status='sent', attachments in R2 ``` -------------------------------- ### Status Update Flow Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Illustrates the flow for updating message status, initiated by a Resend webhook to update D1. ```text Status: Resend webhook → /webhooks/resend → D1 status update ``` -------------------------------- ### Update Draft Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Updates an existing draft email. Requires `X-API-Key` header. ```APIDOC ## PUT /api/drafts/:id ### Description Update a draft ### Method PUT ### Endpoint /api/drafts/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the draft to update. ### Request Body - **to** (string) - Optional - Recipient email address. - **cc** (string) - Optional - CC recipient email address. - **bcc** (string) - Optional - BCC recipient email address. - **subject** (string) - Optional - Subject of the draft. - **body_text** (string) - Optional - Plain text content of the draft. - **thread_id** (string) - Optional - The ID of the thread this draft belongs to. ``` -------------------------------- ### Send Email Source: https://github.com/kritsanan1/clawpost/blob/main/SKILL.md Send a new email message with specified recipients, subject, and body. ```python send_email(to: "alice@example.com", subject: "Hello", body: "Message text") ``` -------------------------------- ### Update Alias - Add Destination Source: https://github.com/kritsanan1/clawpost/blob/main/SKILL.md Add a new destination to an existing alias without removing current ones. ```bash clawpost alias update --add-to extra@example.com ``` -------------------------------- ### List Approved Messages Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves a list of approved messages with optional filtering and pagination. Requires `X-API-Key` header. ```APIDOC ## GET /api/messages ### Description List approved messages ### Method GET ### Endpoint /api/messages ### Query Parameters - **limit** (integer) - Optional - Maximum number of messages to return. - **offset** (integer) - Optional - Number of messages to skip. - **direction** (string) - Optional - Direction of sorting (e.g., 'asc', 'desc'). - **from** (string) - Optional - Filter by sender email. - **to** (string) - Optional - Filter by recipient email. - **label** (string) - Optional - Filter by label. - **include_archived** (boolean) - Optional - Whether to include archived messages. ``` -------------------------------- ### Full-text Search Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Performs a full-text search across messages. Requires `X-API-Key` header. ```APIDOC ## GET /api/search ### Description Full-text search ### Method GET ### Endpoint /api/search ### Query Parameters - **q** (string) - Required - The search query. - **limit** (integer) - Optional - Maximum number of results to return. - **include_archived** (boolean) - Optional - Whether to include archived messages in the search. ``` -------------------------------- ### Buffer Raw Message for Alias Fan-out Source: https://github.com/kritsanan1/clawpost/blob/main/docs/domain-and-routing-gaps.md This fix ensures that the raw message is fully buffered before being forwarded to multiple alias destinations. This prevents the stream from being consumed by the first recipient, ensuring subsequent recipients receive the complete message. ```typescript async forwardAliasMessage(message: Message, alias: Alias) { // Buffer message.raw first to ensure it's available for all destinations const rawMessageBuffer = await streamToArrayBuffer(message.raw); // If there's only one destination, short-circuit for efficiency if (alias.destinations.length === 1) { return message.forward(alias.destinations[0], rawMessageBuffer); } // Forward to all destinations using the buffered content const forwardPromises = alias.destinations.map(destination => { // Re-create a readable stream from the buffer for each forward const messageStream = new ReadableStream({ start(controller) { controller.enqueue(new Uint8Array(rawMessageBuffer)); controller.close(); } }); return message.forward(destination, messageStream); }); await Promise.all(forwardPromises); } ``` -------------------------------- ### Delete Alias Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Deletes an alias. Requires `X-API-Key` header. ```APIDOC ## DELETE /api/aliases/:id ### Description Delete alias ### Method DELETE ### Endpoint /api/aliases/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the alias to delete. ``` -------------------------------- ### List Unapproved Messages Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves a list of unapproved messages (metadata only). Requires `X-API-Key` header. ```APIDOC ## GET /api/pending ### Description List unapproved messages (metadata only) ### Method GET ### Endpoint /api/pending ``` -------------------------------- ### Request Routing in src/index.ts Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Defines how incoming requests are routed based on the path and authentication method. Handles /mcp requests separately from other paths. ```typescript // Request routing in src/index.ts: // - /mcp → Bearer token auth → EmailMCP.serve() (Durable Object) // - Everything else → Hono app (src/api.ts) which handles /api/* with X-API-Key auth and /webhooks/* unauthenticated ``` -------------------------------- ### Update Inbox Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Updates an existing inbox. Requires `X-API-Key` header. ```APIDOC ## PUT /api/inboxes/:id ### Description Update inbox ### Method PUT ### Endpoint /api/inboxes/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the inbox to update. ### Request Body - **email** (string) - Optional - The email address for the inbox. - **enabled** (boolean) - Optional - Whether the inbox is enabled. ``` -------------------------------- ### Reply to Message Source: https://github.com/kritsanan1/clawpost/blob/main/SKILL.md Reply to an existing message using its ID. Threading headers are handled automatically. ```python reply_to_message(id: "uuid-of-message", body: "Reply text") ``` -------------------------------- ### Shared Service Function Usage Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Highlights that both API routes and MCP tools utilize shared service functions for common operations like sending mail, managing labels, archiving, searching, and drafts. ```typescript // Both API routes and MCP tools call shared service functions: src/mail.ts (send/reply), src/labels.ts, src/archive.ts, src/search.ts, src/drafts.ts ``` -------------------------------- ### Outbound Email Flow Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Illustrates the flow of an outbound email, initiated by an MCP tool or API, through Cloudflare Email Service or Resend to D1 and R2. ```text Outbound: MCP tool / API → CF Email Service or Resend → D1 + R2 ``` -------------------------------- ### Inbound Email Processing Logic Source: https://github.com/kritsanan1/clawpost/blob/main/CLAUDE.md Describes the data flow for inbound emails, including alias and inbox checks, parsing, storage, and webhook dispatch. ```typescript // Inbound email (src/email.ts): checks alias table first (forward if matched), then inbox table (store if matched), then rejects if the domain is managed but the address isn't, otherwise stores as catch-all. Parsed via postal-mime → D1 + R2 → webhook dispatch ``` -------------------------------- ### Create Alias with Rollback Source: https://github.com/kritsanan1/clawpost/blob/main/docs/domain-and-routing-gaps.md Ensures alias creation is failure-atomic. If destination synchronization fails after the Cloudflare rule and alias row are persisted, the operation is rolled back by deleting the alias row and the Cloudflare rule. ```typescript async createAlias(args: { // ... other args aliasName: string; destinations: string[]; }) { const { zoneId, aliasName, destinations } = args; const pattern = `${aliasName}@${env.CLAWPOST_DOMAIN}`; // Create Cloudflare rule and insert alias row first const route = await createOrUpdateWorkerRoute({ zoneId, routeId: null, pattern, script: env.CLOUDFLARE_WORKER_NAME, enabled: true, custom_metadata: { clawpost_alias_id: "" } // Placeholder }); let alias: Alias; try { alias = await db.insertInto('aliases').values({ // ... values cf_rule_id: route.id, // ... }).returningAll().executeTakeFirstOrThrow(); // Attempt to sync destinations await replaceAliasDestinations(alias.id, destinations); } catch (e) { // Rollback: Delete alias row and Cloudflare rule on failure if (alias) { await db.deleteFrom('aliases').where(eq(aliases.id, alias.id)).execute(); } await deleteWorkerRoute(zoneId, route.id); throw e; } return alias; } ``` -------------------------------- ### Read Approved Message Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves a specific approved message, including its attachments and labels. Requires `X-API-Key` header. ```APIDOC ## GET /api/messages/:id ### Description Read approved message + attachments + labels ### Method GET ### Endpoint /api/messages/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the message to retrieve. ``` -------------------------------- ### Read Inbox Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Retrieves details of a specific inbox. Requires `X-API-Key` header. ```APIDOC ## GET /api/inboxes/:id ### Description Read inbox ### Method GET ### Endpoint /api/inboxes/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the inbox to retrieve. ``` -------------------------------- ### Update Alias - Replace Destinations Source: https://github.com/kritsanan1/clawpost/blob/main/SKILL.md Update an existing alias to replace all its current destinations with new ones. ```bash clawpost alias update --to new@example.com --to second@example.com ``` -------------------------------- ### Create or Update Worker Route Source: https://github.com/kritsanan1/clawpost/blob/main/docs/domain-and-routing-gaps.md This function is responsible for creating or updating a worker route. It now includes checks to prevent duplicate rules by listing existing exact-address rules before making any changes. ```typescript async createOrUpdateWorkerRoute(args: { zoneId: string; routeId: string | null; pattern: string; script: string; enabled: boolean; custom_metadata: Record; }) { const { zoneId, routeId, pattern, script, enabled, custom_metadata } = args; // Check for existing rules that match the exact pattern const existingRoutes = await listWorkerRoutes(zoneId, { pattern, // Only fetch routes that target this worker script }); if (existingRoutes.some(r => r.pattern === pattern)) { throw new HttpError(400, `Route with pattern "${pattern}" already exists for this worker.`); } // ... rest of the function to create or update the route ``` -------------------------------- ### Update Inbox with Compensation Source: https://github.com/kritsanan1/clawpost/blob/main/docs/domain-and-routing-gaps.md Handles updating an existing inbox. If the database update fails after the Cloudflare rule has been modified, the function attempts to restore the previous Cloudflare rule configuration. ```typescript async updateInbox(args: { // ... other args inboxId: string; domain?: string; emailLocalPart?: string; }) { const { inboxId, domain, emailLocalPart } = args; const existingInbox = await db.query.inboxes.findFirst({ where: (inboxes, { eq }) => eq(inboxes.id, inboxId), columns: { cf_rule_id: true } }); if (!existingInbox) { throw new HttpError(404, 'Inbox not found'); } // ... logic to determine new pattern and zoneId ... const newPattern = `${emailLocalPart}@${domain}`; const zoneId = await getZoneIdByDomain(domain); // Mutate the Cloudflare rule first const previousRoute = await updateWorkerRoute(zoneId, existingInbox.cf_rule_id, { pattern: newPattern, // ... other fields }); try { // Update the DB row const updatedInbox = await db.updateTable('inboxes') .set({ // ... new values cf_rule_id: existingInbox.cf_rule_id // Keep the same rule ID }) .where(eq(inboxes.id, inboxId)) .returningAll() .executeTakeFirstOrThrow(); return updatedInbox; } catch (e) { // Compensation: Restore the previous Cloudflare rule if DB update fails await updateWorkerRoute(zoneId, existingInbox.cf_rule_id, { pattern: previousRoute.pattern, // Restore previous pattern // ... restore other fields from previousRoute }); throw e; } } ``` -------------------------------- ### Resend Delivery Status Webhook Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Resends a delivery status webhook. This endpoint is unauthenticated but token-verified. ```APIDOC ## POST /webhooks/resend ### Description Resend delivery status webhook ### Method POST ### Endpoint /webhooks/resend ### Query Parameters - **token** (string) - Required - The token for webhook verification. ``` -------------------------------- ### Delete Draft Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Deletes a draft email. Requires `X-API-Key` header. ```APIDOC ## DELETE /api/drafts/:id ### Description Delete a draft ### Method DELETE ### Endpoint /api/drafts/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the draft to delete. ``` -------------------------------- ### Update Alias with Rollback Source: https://github.com/kritsanan1/clawpost/blob/main/docs/domain-and-routing-gaps.md Provides rollback safety for alias updates. If destination synchronization fails after the Cloudflare rule and alias record have been modified, the alias record is restored to its previous state, and the Cloudflare rule is best-effort restored. ```typescript async updateAlias(args: { // ... other args aliasId: string; destinations?: string[]; }) { const { aliasId, destinations } = args; const existingAlias = await db.query.aliases.findFirst({ where: (aliases, { eq }) => eq(aliases.id, aliasId), with: { destinations: true } // Assuming destinations are related }); if (!existingAlias) { throw new HttpError(404, 'Alias not found'); } const zoneId = await getZoneIdByDomain(existingAlias.domain); // Assuming domain is on alias const originalAliasData = JSON.parse(JSON.stringify(existingAlias)); // Deep clone for rollback // Mutate Cloudflare rule and alias record first const updatedRoute = await updateWorkerRoute(zoneId, existingAlias.cf_rule_id, { // ... new rule details based on args }); try { const updatedAlias = await db.updateTable('aliases') .set({ // ... new alias values }) .where(eq(aliases.id, aliasId)) .returningAll() .executeTakeFirstOrThrow(); // Attempt to sync destinations if (destinations) { await replaceAliasDestinations(aliasId, destinations); } return updatedAlias; } catch (e) { // Compensation: Restore alias row and best-effort restore Cloudflare rule await db.updateTable('aliases') .set(originalAliasData) .where(eq(aliases.id, aliasId)) .execute(); await updateWorkerRoute(zoneId, existingAlias.cf_rule_id, { // Restore previous rule details from originalAliasData.cf_rule_details // This might involve fetching the original rule details if not stored directly }); throw e; } } ``` -------------------------------- ### Resend Delivery Status Webhook Handler Source: https://github.com/kritsanan1/clawpost/blob/main/README.md The endpoint and expected token for receiving Resend delivery status webhooks. Updates the message status in D1. ```text Clawpost receives Resend delivery webhooks at `POST /webhooks/resend?token=` and updates the message `status` field: ``` -------------------------------- ### Delete Inbox Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Deletes an inbox. Requires `X-API-Key` header. ```APIDOC ## DELETE /api/inboxes/:id ### Description Delete inbox ### Method DELETE ### Endpoint /api/inboxes/:id ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the inbox to delete. ``` -------------------------------- ### Update and Disable a Forwarding Alias Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Updates an existing forwarding alias by disabling it. Replace `` with the actual alias ID. ```bash node dist/clawpost.js alias update --disable ``` -------------------------------- ### Update Alias - Remove Destination Source: https://github.com/kritsanan1/clawpost/blob/main/SKILL.md Remove a specific destination from an existing alias. ```bash clawpost alias update --remove-to old@example.com ``` -------------------------------- ### Delete Inbox with Compensation Source: https://github.com/kritsanan1/clawpost/blob/main/docs/domain-and-routing-gaps.md Handles the deletion of an inbox. If the database deletion fails after the Cloudflare rule has been removed, this function attempts to recreate the rule and update the stored `cf_rule_id`. ```typescript async deleteInbox(inboxId: string) { const existingInbox = await db.query.inboxes.findFirst({ where: (inboxes, { eq }) => eq(inboxes.id, inboxId), columns: { cf_rule_id: true, email: true } // Assuming 'email' column stores the full address }); if (!existingInbox) { throw new HttpError(404, 'Inbox not found'); } const { cf_rule_id, email } = existingInbox; const zoneId = await getZoneIdByDomain(email.split('@')[1]); // Extract domain from email // Delete the Cloudflare rule first await deleteWorkerRoute(zoneId, cf_rule_id); try { // Delete the DB row await db.deleteFrom('inboxes').where(eq(inboxes.id, inboxId)).executeTakeFirst(); } catch (e) { // Compensation: Recreate the rule and update the stored cf_rule_id if DB delete fails const recreatedRoute = await createOrUpdateWorkerRoute({ zoneId, routeId: null, // Create new route pattern: email, // Use the original email pattern script: env.CLOUDFLARE_WORKER_NAME, enabled: true, custom_metadata: { clawpost_inbox_id: inboxId } // Associate with the inbox ID }); // Update the DB with the new rule ID (best-effort) await db.updateTable('inboxes') .set({ cf_rule_id: recreatedRoute.id }) .where(eq(inboxes.id, inboxId)) .execute(); throw e; } } ``` -------------------------------- ### Delete Inbox Source: https://github.com/kritsanan1/clawpost/blob/main/SKILL.md Delete a managed inbox. ```bash clawpost inbox delete ``` -------------------------------- ### Remove Label from Message Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Removes a specific label from an approved message. Requires `X-API-Key` header. ```APIDOC ## DELETE /api/messages/:id/labels/:label ### Description Remove a label from an approved message ### Method DELETE ### Endpoint /api/messages/:id/labels/:label ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the message. - **label** (string) - Required - The label to remove. ``` -------------------------------- ### Remove Approved Sender Source: https://github.com/kritsanan1/clawpost/blob/main/README.md Removes an email sender from the approved list. Requires `X-API-Key` header. ```APIDOC ## DELETE /api/approved-senders/:email ### Description Remove approved sender ### Method DELETE ### Endpoint /api/approved-senders/:email ### Parameters #### Path Parameters - **email** (string) - Required - The email address of the sender to remove. ``` -------------------------------- ### Message Received Webhook Payload Source: https://github.com/kritsanan1/clawpost/blob/main/README.md The JSON payload sent to the configured WEBHOOK_URL for every inbound email. Includes event details and message data. ```json { "event": "message.received", "data": { "id": "...", "thread_id": "...", "from": "...", "to": "...", "subject": "...", "direction": "inbound", "approved": 0, "created_at": 1234567890 }, "timestamp": 1234567890 } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.