<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kumar Deepanshu</title>
    <description>The latest articles on DEV Community by Kumar Deepanshu (@kumard3).</description>
    <link>https://dev.to/kumard3</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F547736%2F36cfad3b-d1ea-4da8-bfcb-edb46455a374.jpeg</url>
      <title>DEV Community: Kumar Deepanshu</title>
      <link>https://dev.to/kumard3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kumard3"/>
    <language>en</language>
    <item>
      <title>Per-User Inbox Provisioning for Multi-Tenant Agent SaaS</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Mon, 15 Jun 2026 18:00:05 +0000</pubDate>
      <link>https://dev.to/kumard3/per-user-inbox-provisioning-for-multi-tenant-agent-saas-2nd1</link>
      <guid>https://dev.to/kumard3/per-user-inbox-provisioning-for-multi-tenant-agent-saas-2nd1</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/multi-tenant-inbox-provisioning-agent-saas/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you're building an agent product — personal shopping agents, AI recruiters, autonomous SDRs — every end user will eventually need their own agent, and every agent needs its own inbox. The naive approach (one shared inbox, filter by user) collapses fast. Here's the pattern that scales.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule: one inbox per (tenant, agent) pair
&lt;/h2&gt;

&lt;p&gt;For a user running 3 agents, you provision 3 Lumbox inboxes. Each has isolated state, its own OTP stream, its own webhook routing. Tenancy becomes a property of the inbox, not a filter on a shared stream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;provisionAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;agentConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AgentConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lumbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;agentConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;agentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agentConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;agents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agentConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Webhook routing
&lt;/h2&gt;

&lt;p&gt;One webhook endpoint on your side, Lumbox posts every received email with the inbox's &lt;code&gt;metadata&lt;/code&gt;. You look up which tenant + agent by the &lt;code&gt;userId&lt;/code&gt; in the metadata and dispatch accordingly. No filtering, no risk of cross-tenant leaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom domain per tenant
&lt;/h2&gt;

&lt;p&gt;For pro-tier customers, let them bring their own domain. Lumbox supports multi-domain inbox provisioning under a single org — the inbox is created on &lt;code&gt;@theirdomain.com&lt;/code&gt;, but your billing + usage aggregation stays on the single parent org.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lifecycle
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Soft delete&lt;/strong&gt; when a user pauses an agent. Keep the inbox; stop webhook routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard delete&lt;/strong&gt; when they churn. Lumbox purges the inbox and all history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transfer&lt;/strong&gt; supported via org migration — inbox stays, billing moves.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Billing
&lt;/h2&gt;

&lt;p&gt;Lumbox's metering reports per-inbox usage. Roll it up to tenant-level for your own invoicing, or expose it in a per-user dashboard. No per-tenant infrastructure to run — it's all one API call. &lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>multitenant</category>
      <category>saas</category>
      <category>architecture</category>
      <category>agents</category>
    </item>
    <item>
      <title>Web Bot Auth: Signing Outbound Agent Webhooks with HTTP Message Signatures</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Fri, 12 Jun 2026 18:00:29 +0000</pubDate>
      <link>https://dev.to/kumard3/web-bot-auth-signing-outbound-agent-webhooks-with-http-message-signatures-5cc1</link>
      <guid>https://dev.to/kumard3/web-bot-auth-signing-outbound-agent-webhooks-with-http-message-signatures-5cc1</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/web-bot-auth-webhook-signing-rfc-9421/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Your SaaS sends webhooks. The receiver's endpoint is public. Anyone who discovers the URL can POST to it pretending to be you. Shared HMAC secrets are the common fix, but they require out-of-band key exchange and they don't rotate well. RFC 9421 HTTP Message Signatures — the "Web Bot Auth" pattern — is the better answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Web Bot Auth gives you
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You publish an Ed25519 public key at &lt;code&gt;/.well-known/http-message-signatures-directory&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You sign outbound requests with the matching private key using the &lt;code&gt;Signature-Input&lt;/code&gt; and &lt;code&gt;Signature&lt;/code&gt; headers.&lt;/li&gt;
&lt;li&gt;The receiver fetches your JWKS (once, cache it) and verifies every incoming request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No shared secret. No one-to-one key exchange. Rotate by publishing a new &lt;code&gt;kid&lt;/code&gt; in the JWKS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The signature in practice
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Signature-Input: sig1=("@method" "@target-uri" "content-digest" "date");\
    keyid="FOhe9v_YLODSykvK-u7VZ5K1-pI85ZN64swJn1YiLcQ";\
    alg="ed25519";\
    created=1749744000
Signature: sig1=:MEUCIQDX...:
Content-Digest: sha-256=:X48E9qOok...:
Date: Tue, 12 Jun 2026 12:00:00 GMT
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Signing side (Node)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sign&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http-message-signatures&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;privateKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;keyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FOhe9v_YLODSykvK-u7VZ5K1-pI85ZN64swJn1YiLcQ&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;alg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ed25519&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@method&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@target-uri&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-digest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verifying side
&lt;/h2&gt;

&lt;p&gt;The receiver fetches your JWKS from &lt;code&gt;/.well-known/http-message-signatures-directory&lt;/code&gt;, caches it, and runs the signature verification. Any tampered body, replayed request (outside the &lt;code&gt;created&lt;/code&gt; window), or wrong &lt;code&gt;keyid&lt;/code&gt; fails verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why agents especially need this
&lt;/h2&gt;

&lt;p&gt;When an AI agent receives a webhook from a third party, it has no way to know if the payload is legit unless the sender is cryptographically identified. Web Bot Auth is the pattern Cloudflare, Anthropic, and others are converging on for agent-to-agent trust. &lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt; signs every outbound webhook with Ed25519 so your agent can trust what arrives.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webhooks</category>
      <category>rfc9421</category>
      <category>webbotauth</category>
    </item>
    <item>
      <title>OAuth for MCP Servers: Authenticating Agents, Not Humans</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Tue, 09 Jun 2026 18:00:20 +0000</pubDate>
      <link>https://dev.to/kumard3/oauth-for-mcp-servers-authenticating-agents-not-humans-1jn5</link>
      <guid>https://dev.to/kumard3/oauth-for-mcp-servers-authenticating-agents-not-humans-1jn5</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/mcp-server-oauth-authentication-agents/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The Model Context Protocol spec recommends OAuth 2.1 + Dynamic Client Registration (RFC 7591) for server authentication. In practice, 90% of MCP servers ship with static API keys. That works for single-user local setups. It breaks the moment you want to expose an MCP server to many agents, many users, or a public marketplace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why OAuth beats API keys for MCP
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scoped access.&lt;/strong&gt; An agent can request &lt;code&gt;inboxes.read&lt;/code&gt; but not &lt;code&gt;inboxes.delete&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revocable without rotating.&lt;/strong&gt; A compromised token is revoked; the long-lived secret never leaves the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-user attribution.&lt;/strong&gt; Every tool call has a user identity. Audit becomes possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic client registration.&lt;/strong&gt; An MCP client (Claude, Cursor) can self-register without a human configuring the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The minimum viable OAuth surface
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/.well-known/oauth-authorization-server&lt;/code&gt; — RFC 8414 metadata.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/.well-known/oauth-protected-resource&lt;/code&gt; — RFC 9728 linking resource to auth server.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/oauth/register&lt;/code&gt; — RFC 7591 Dynamic Client Registration.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/oauth/authorize&lt;/code&gt; — standard OAuth 2.1 authorization code flow with PKCE.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/oauth/token&lt;/code&gt; — token exchange.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Token design for agents
&lt;/h2&gt;

&lt;p&gt;Short-lived access tokens (15 minutes), long-lived refresh tokens (30 days, rotating on use), narrow scopes per tool class. Don't issue a token with &lt;code&gt;*&lt;/code&gt; scope unless the client literally needs every tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lumbox approach
&lt;/h2&gt;

&lt;p&gt;Lumbox's MCP server is moving from API-key auth to full OAuth 2.1 + DCR. When an agent installs &lt;code&gt;@lumbox/mcp-server&lt;/code&gt;, the client auto-registers, pops an OAuth consent screen once, and gets scoped tokens for the tools it asked for. No hand-editing a config file with a key. &lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>oauth</category>
      <category>security</category>
      <category>agents</category>
    </item>
    <item>
      <title>Event-Driven Email Agents with Inngest and Lumbox</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Sat, 06 Jun 2026 18:00:07 +0000</pubDate>
      <link>https://dev.to/kumard3/event-driven-email-agents-with-inngest-and-lumbox-2dcd</link>
      <guid>https://dev.to/kumard3/event-driven-email-agents-with-inngest-and-lumbox-2dcd</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/inngest-event-driven-email-agents-lumbox/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Inngest took the parts of writing a real application that always suck — queues, retries, cron, long-running jobs — and collapsed them into a single primitive: the event-driven function. It's the right partner for Lumbox, because inbound email is the event layer most agent backends are missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The wiring
&lt;/h2&gt;

&lt;p&gt;Lumbox fires a webhook when mail arrives. Point that webhook at an Inngest event endpoint. Inngest's function handles the parsed email with full retry + observability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Point Lumbox webhook at: https://inn.gs/e/YOUR_INGEST_KEY&lt;/span&gt;
&lt;span class="c1"&gt;// Events emitted as "lumbox.email.received"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inngest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleInboundEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inngest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;handle-inbound-email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lumbox.email.received&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extracted&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;extracted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resolve-pending-signup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;resolvePendingSignup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extracted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;log-to-crm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;logEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scheduled follow-ups
&lt;/h2&gt;

&lt;p&gt;Use Inngest's &lt;code&gt;step.sleep&lt;/code&gt; to schedule a follow-up email in a day, a week, a month. Lumbox handles the send, Inngest handles the durability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;followUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inngest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;follow-up&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;signup.completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wait-3-days&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send-followup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;lumbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AGENT_INBOX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How's it going?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Checking in from your onboarding agent.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>inngest</category>
      <category>eventdriven</category>
      <category>email</category>
      <category>webhooks</category>
    </item>
    <item>
      <title>Durable Email Workflows with Temporal and Lumbox</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Wed, 03 Jun 2026 18:00:34 +0000</pubDate>
      <link>https://dev.to/kumard3/durable-email-workflows-with-temporal-and-lumbox-60d</link>
      <guid>https://dev.to/kumard3/durable-email-workflows-with-temporal-and-lumbox-60d</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/temporal-durable-email-workflows-lumbox/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Temporal solves the problem every async workflow eventually hits: what happens if the process dies mid-flow? For email, it's acute — you kick off a signup, the worker crashes, and now you have a Lumbox inbox waiting for an OTP that no one is listening for. Temporal activities make that durable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern: inbox creation as an activity
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;proxyActivities&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@temporalio/workflow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;activities&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./activities&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createInbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;submitSignup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitForOtp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;submitOtp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;proxyActivities&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;activities&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;startToCloseTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5 minutes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;signupWorkflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createInbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`signup-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;submitSignup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;otp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitForOtp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;submitOtp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Long waits are fine
&lt;/h2&gt;

&lt;p&gt;Some verification flows send a link that expires in 24 hours. Temporal can hold a workflow open for days with no resource cost — when the worker comes back, the state picks up. Lumbox inboxes don't expire unless you tell them to, so a workflow that sleeps for a day and then checks the inbox works exactly as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retries are automatic
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;waitForOtp&lt;/code&gt; times out, Temporal retries per your activity policy. Lumbox's timeout is server-side — the HTTP call cleanly returns after the timeout window, and Temporal handles the rest. &lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>temporal</category>
      <category>durableexecution</category>
      <category>email</category>
      <category>workflows</category>
    </item>
    <item>
      <title>Mastra + Lumbox: TypeScript-First Agent Email in 30 Lines</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Sun, 31 May 2026 18:00:38 +0000</pubDate>
      <link>https://dev.to/kumard3/mastra-lumbox-typescript-first-agent-email-in-30-lines-3n1k</link>
      <guid>https://dev.to/kumard3/mastra-lumbox-typescript-first-agent-email-in-30-lines-3n1k</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/mastra-email-tool-lumbox/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Mastra filled a gap: a genuinely good TypeScript framework for agents. LangChain.js never felt like a first-class citizen; Mastra does. Here's how to bolt Lumbox on in about 30 lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining email tools
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createTool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mastra/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Lumbox&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lumbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lumbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Lumbox&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LUMBOX_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createInbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create_inbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create a fresh email inbox for a specific task.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;purpose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lumbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;purpose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;waitForOtp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wait_for_otp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Block until a verification OTP arrives.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lumbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForOtp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;otp&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The agent
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Agent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mastra/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signupAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;signup_agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You sign users up for services. Use create_inbox, then wait_for_otp.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;anthropic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;claude-opus-4-7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createInbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitForOtp&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Workflow orchestration
&lt;/h2&gt;

&lt;p&gt;Mastra's workflows give you checkpointed, resumable flows. A signup that stalls at the OTP step can be resumed hours later — Lumbox keeps the inbox alive and the OTP retrievable. &lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mastra</category>
      <category>typescript</category>
      <category>aiagents</category>
      <category>email</category>
    </item>
    <item>
      <title>LlamaIndex + Lumbox: Treat Your Inbox as a Queryable Knowledge Source</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Thu, 28 May 2026 18:00:43 +0000</pubDate>
      <link>https://dev.to/kumard3/llamaindex-lumbox-treat-your-inbox-as-a-queryable-knowledge-source-ejd</link>
      <guid>https://dev.to/kumard3/llamaindex-lumbox-treat-your-inbox-as-a-queryable-knowledge-source-ejd</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/llamaindex-email-tool-lumbox/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;LlamaIndex made RAG something you can ship in an afternoon. Load docs into an index, attach an LLM, query. The gap most teams hit isn't the framework — it's finding a good data source. Your agent's own Lumbox inbox is a better source than most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the inbox is good RAG fodder
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It's high-signal, low-noise (it's already filtered by being sent to you).&lt;/li&gt;
&lt;li&gt;It's structured — sender, date, subject, thread ID are all fields.&lt;/li&gt;
&lt;li&gt;It grows organically as the agent does its work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wiring it up
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Document&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;lumbox&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Lumbox&lt;/span&gt;

&lt;span class="n"&gt;lumbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Lumbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LUMBOX_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_inbox_as_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inbox_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lumbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inbox_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inbox_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;subject&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;received_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;thread_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thread_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_inbox_as_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_inbox_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;query_engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_query_engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What did Acme say about the Q3 timeline?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Keep it fresh with webhooks
&lt;/h2&gt;

&lt;p&gt;Subscribe to Lumbox's &lt;code&gt;email.received&lt;/code&gt; webhook and incrementally upsert new messages into the index. No re-indexing, no polling. &lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>llamaindex</category>
      <category>rag</category>
      <category>python</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>AX (Agent Experience): What Email APIs Should Look Like for Agents, Not Humans</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Mon, 25 May 2026 18:00:38 +0000</pubDate>
      <link>https://dev.to/kumard3/ax-agent-experience-what-email-apis-should-look-like-for-agents-not-humans-1cgo</link>
      <guid>https://dev.to/kumard3/ax-agent-experience-what-email-apis-should-look-like-for-agents-not-humans-1cgo</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/ax-agent-experience-email-apis/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Resend coined Agent Experience — AX — as the agent-first parallel to DX. The idea: APIs designed for humans reading docs on a Tuesday afternoon look different from APIs designed for LLMs calling tools in a tight loop. For email APIs, the gap is huge.&lt;/p&gt;

&lt;h2&gt;
  
  
  What bad AX looks like in email APIs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polling required.&lt;/strong&gt; Agent burns tokens checking "any new mail?" every 5 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raw MIME.&lt;/strong&gt; Agent has to parse multipart boundaries before it can read the body.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No first-class OTP field.&lt;/strong&gt; Agent runs regex on HTML to find the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opaque errors.&lt;/strong&gt; Agent gets &lt;code&gt;"500: internal error"&lt;/code&gt; with no recovery guidance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No idempotency.&lt;/strong&gt; Agent retries and sends duplicates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What good AX looks like
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Long-poll endpoints.&lt;/strong&gt; One call, blocks until event or timeout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured extraction.&lt;/strong&gt; OTP, links, attachments, category — all as first-class JSON fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typed errors.&lt;/strong&gt; &lt;code&gt;{"error": "timeout", "retryable": true, "wait_ms": 5000}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency keys.&lt;/strong&gt; Every mutating endpoint accepts one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP-native.&lt;/strong&gt; Tools are discoverable via a manifest, not scraped from HTML docs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Machine-readable docs.&lt;/strong&gt; OpenAPI + .well-known/agent-skills + llms.txt.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lumbox's take
&lt;/h2&gt;

&lt;p&gt;Every Lumbox endpoint was designed with a question: "how would a tool-calling LLM invoke this?" That's why &lt;code&gt;waitForOtp&lt;/code&gt; exists instead of a &lt;code&gt;listMessages&lt;/code&gt; + filter pattern. It's why responses include &lt;code&gt;otp&lt;/code&gt;, &lt;code&gt;links&lt;/code&gt;, and &lt;code&gt;category&lt;/code&gt; alongside the raw body. It's why we publish an MCP server on day one.&lt;/p&gt;

&lt;p&gt;AX isn't a checkbox. It's the shape of the whole API surface. &lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>agentexperience</category>
      <category>apidesign</category>
      <category>aiagents</category>
      <category>email</category>
    </item>
    <item>
      <title>Email as Memory for AI Agents</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Fri, 22 May 2026 18:00:18 +0000</pubDate>
      <link>https://dev.to/kumard3/email-as-memory-for-ai-agents-850</link>
      <guid>https://dev.to/kumard3/email-as-memory-for-ai-agents-850</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/email-as-memory-ai-agents-lumbox/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every agent framework now ships a memory module. Vectors, summaries, reflection loops, graph-based recall. They all miss the simplest and most reliable memory primitive on the internet: the email inbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  What email gets right as memory
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Append-only by default.&lt;/strong&gt; You don't lose history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Addressable.&lt;/strong&gt; Every message has a stable ID and thread ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Searchable.&lt;/strong&gt; Full-text search is built in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured where it matters.&lt;/strong&gt; Sender, subject, date, attachments — all first-class fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull model.&lt;/strong&gt; The agent reads when it needs, not streaming 24/7.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interoperable.&lt;/strong&gt; The rest of the world writes to the inbox without you asking.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The agent-memory pattern
&lt;/h2&gt;

&lt;p&gt;Give the agent an inbox. When the agent takes an action, send a record email to itself summarizing what it did. When it needs to recall, query the inbox. Lumbox's API gives you list, search, thread, and filter — the full memory surface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// agent acts&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lumbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inbox_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[memory] signed up for stripe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Account created with card ending 4242, verified at 14:22Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// later, agent recalls&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;memories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lumbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inbox_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[memory]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;since&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-05-01&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When it beats a vector store
&lt;/h2&gt;

&lt;p&gt;When you need &lt;em&gt;verbatim&lt;/em&gt; recall, temporal ordering, and cross-agent handoffs. Vector stores excel at fuzzy semantic similarity. Inboxes excel at "what did I do last Tuesday and who did I talk to about it."&lt;/p&gt;

&lt;h2&gt;
  
  
  Hybrid is fine
&lt;/h2&gt;

&lt;p&gt;Use the inbox for durable, addressable, audit-able memory. Use a vector store for similarity search over summaries. They complement each other. &lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>memory</category>
      <category>architecture</category>
      <category>aiagents</category>
      <category>email</category>
    </item>
    <item>
      <title>Pydantic AI + Lumbox: Type-Safe Email Tools for Agents</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Tue, 19 May 2026 18:00:05 +0000</pubDate>
      <link>https://dev.to/kumard3/pydantic-ai-lumbox-type-safe-email-tools-for-agents-4ea5</link>
      <guid>https://dev.to/kumard3/pydantic-ai-lumbox-type-safe-email-tools-for-agents-4ea5</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/pydantic-ai-email-agent-lumbox/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pydantic AI leaned into the one thing Python agent frameworks were missing: type safety at the tool boundary. Define a tool as a function with Pydantic-validated inputs and outputs, and the framework handles the LLM-side schema and the runtime-side coercion. It's the right shape for production agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lumbox as Pydantic AI tools
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;lumbox&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Lumbox&lt;/span&gt;

&lt;span class="n"&gt;lumbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Lumbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LUMBOX_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Inbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;inbox_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OtpResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;received_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="n"&gt;email_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic:claude-opus-4-7&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You handle email verification for signup flows.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@email_agent.tool_plain&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_inbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;purpose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Inbox&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create a fresh inbox for a verification task.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;inbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lumbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;display_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;purpose&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Inbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inbox_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@email_agent.tool_plain&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_otp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inbox_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OtpResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Block until an OTP arrives in the inbox.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lumbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_otp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inbox_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;OtpResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;otp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;received_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;received_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why type-safe tools matter for email
&lt;/h2&gt;

&lt;p&gt;When an agent calls &lt;code&gt;wait_for_otp&lt;/code&gt; with a timeout, you don't want a runtime error because the LLM passed &lt;code&gt;"60s"&lt;/code&gt; instead of &lt;code&gt;60&lt;/code&gt;. Pydantic coerces at the tool boundary. Same for inbox IDs — Pydantic ensures you always get a well-formed &lt;code&gt;inbox_id&lt;/code&gt;, never &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error handling
&lt;/h2&gt;

&lt;p&gt;If the OTP wait times out, the tool raises — Pydantic AI surfaces the typed error back to the agent, which can retry or escalate. Cleaner than parsing string error messages out of generic HTTP responses. &lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pydanticai</category>
      <category>python</category>
      <category>typesafety</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>Infraforge vs Lumbox: Deliverability-First vs Agent-Native Email</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Sat, 16 May 2026 18:01:06 +0000</pubDate>
      <link>https://dev.to/kumard3/infraforge-vs-lumbox-deliverability-first-vs-agent-native-email-3keb</link>
      <guid>https://dev.to/kumard3/infraforge-vs-lumbox-deliverability-first-vs-agent-native-email-3keb</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/infraforge-vs-lumbox-agent-email/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Infraforge appears on most "best AgentMail alternatives" lists because it targets the deliverability-first segment of email infrastructure. Lumbox targets the AI-agent-first segment. Both valid, different focuses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Infraforge's shape
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Managed outbound email infrastructure — warming pools, IP rotation, reputation management.&lt;/li&gt;
&lt;li&gt;Focused on cold outreach, transactional at scale, and B2B email workflows.&lt;/li&gt;
&lt;li&gt;Strong on deliverability tooling, domain authentication, and sender analytics.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lumbox's shape
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Agent-native — every agent gets its own inbox.&lt;/li&gt;
&lt;li&gt;OTP / magic-link / verification-link extraction built in.&lt;/li&gt;
&lt;li&gt;Long-poll endpoints so agents don't waste tokens polling.&lt;/li&gt;
&lt;li&gt;MCP server so Claude and Cursor can treat Lumbox as a tool out of the box.&lt;/li&gt;
&lt;li&gt;BYOS: bring your own SMTP/domain when you want full deliverability control.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When they overlap
&lt;/h2&gt;

&lt;p&gt;If you're running outbound-heavy agent workflows (cold outreach, mass notification), you need both: Lumbox for agent identity + inbox, and a deliverability layer (either Infraforge, SES with warm IPs, or Lumbox's BYOS mode with your own SMTP). The inbox side and the deliverability side are different problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  When they don't
&lt;/h2&gt;

&lt;p&gt;If you're running inbound-heavy flows — signups, verifications, agent-to-service interactions — Lumbox is the primitive. Deliverability is not the constraint; per-agent isolation and OTP extraction are. &lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>infraforge</category>
      <category>comparison</category>
      <category>emailapi</category>
      <category>deliverability</category>
    </item>
    <item>
      <title>Unipile vs Lumbox: Unified Messaging API vs Agent-First Email</title>
      <dc:creator>Kumar Deepanshu</dc:creator>
      <pubDate>Wed, 13 May 2026 18:01:12 +0000</pubDate>
      <link>https://dev.to/kumard3/unipile-vs-lumbox-unified-messaging-api-vs-agent-first-email-51n3</link>
      <guid>https://dev.to/kumard3/unipile-vs-lumbox-unified-messaging-api-vs-agent-first-email-51n3</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://lumbox.co/blog/unipile-vs-lumbox-unified-email/" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Unipile and Lumbox show up together in "best email API for AI agents" listicles, but they're solving different problems. Here's how to tell which you actually need.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Unipile does
&lt;/h2&gt;

&lt;p&gt;Unipile is a unified communications API. One SDK, many channels — LinkedIn, WhatsApp, Instagram, Telegram, Gmail, Outlook, IMAP. You authenticate &lt;em&gt;your users'&lt;/em&gt; accounts, then send and receive on their behalf across channels. Use case: building a multi-channel inbox product where end users connect their own accounts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Lumbox does
&lt;/h2&gt;

&lt;p&gt;Lumbox provisions &lt;em&gt;net-new&lt;/em&gt; email inboxes for AI agents. Every agent gets a fresh identity — not piggybacking on a user's Gmail, not sharing an account. Plus OTP extraction, long-poll, MCP server, and per-agent isolation. Use case: autonomous agents that need their own email to interact with the web.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick Unipile if...
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You're building a product where end users connect their own accounts.&lt;/li&gt;
&lt;li&gt;You need multi-channel reach (LinkedIn + WhatsApp + email).&lt;/li&gt;
&lt;li&gt;You're building on behalf of humans, not agents.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pick Lumbox if...
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Your agent needs its own email identity, not a user's.&lt;/li&gt;
&lt;li&gt;You need OTP/verification-link extraction out of the box.&lt;/li&gt;
&lt;li&gt;You need long-poll endpoints so agents don't burn tokens polling.&lt;/li&gt;
&lt;li&gt;You need per-agent isolation and fast lifecycle (create/destroy in seconds).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Can you use both?
&lt;/h2&gt;

&lt;p&gt;Yes. A common pattern: use Unipile for the human-facing side of your product (LinkedIn DMs, WhatsApp replies) and Lumbox for the autonomous-agent side (signups, verifications, outbound to third-party APIs). Different problems, different tools. &lt;a href="https://lumbox.co" rel="noopener noreferrer"&gt;lumbox.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>unipile</category>
      <category>comparison</category>
      <category>emailapi</category>
      <category>aiagents</category>
    </item>
  </channel>
</rss>
