We raised $6M in Seed FundingRead more
+
+
+
+
+
+
+
+
Blog/Engineering

3 ways to provision a mailbox for each new customer: catch-all, Workspace seats, or an inbox API

Give each new customer a mailbox at signup three ways: a catch-all, a Google Workspace or Microsoft 365 seat, or an inbox API. Cost, setup time, retries compared.

TL;DR

A product that gives every new customer an email address at signup has three real options. Route a whole domain into one catch-all mailbox, buy a paid office-suite seat for each customer, or call an inbox API that creates a real mailbox inside the signup request. Each one trades cost, speed, and what happens when a signup runs twice.

Your product signs up a customer, and the customer needs an email address of their own. Maybe they forward invoices to it. Maybe an agent working for them sends follow-ups from it and reads the replies. Either way, the address has to exist the moment the account does. The first email can arrive minutes later.

That sounds like a small task until you count. One customer is a settings page. Ten thousand customers is a provisioning system, with a bill attached to every row and a failure mode for every retry. The choice you make on day one decides what each signup costs. It also decides how long a customer waits for a working inbox, and whether a timed-out request leaves two mailboxes behind.

There are three ways to do it. Below is what each one costs per customer and how fast it goes live. Then comes where each one breaks, and the code for the version that runs inside the signup handler.

Which way should you give each new customer a mailbox

Give each new customer a mailbox through an inbox API when they need to send and receive as themselves. Use a catch-all only when mail comes in and nobody replies. A seat per customer on Google Workspace or Microsoft 365 is the third way. It fits a narrow case: fewer than 300 customers who also want the calendar, documents, and chat that come with the seat.

The conditions decide it. Inbound only, modest volume, no reply from each address: catch-all. A few hundred customers who'll log in to Gmail or Outlook like employees: a seat. Every customer, or every customer's agent, holds its own conversations and signups arrive at any hour: an inbox API, either Nylas Agent Accounts or AgentMail.

One question settles most cases. Does the customer ever send? If the answer is no, you don't need a mailbox per customer at all. If it's yes, you need a real one, and the remaining choice is how you pay for it.

How the three ways compare on cost, setup time, and upkeep

Cost per customer is where the three ways split furthest apart. A catch-all costs nothing per signup. A seat costs a monthly license per signup. An inbox API sits between them, with a plan that includes a block of mailboxes and a price for each one past it.

WayCost per customerHow each mailbox is createdWhat the customer getsBest for
Catch-all routing on Google WorkspaceNothing per customer; one mailbox receives everythingNone per customer once the routing rule existsA string in your database, no reply identityInbound-only intake that never replies
Google Workspace Business Starter seat$7 per user per month, at most 300 users on Business plansA Directory API call, then up to 24 hoursA full Gmail account with 30 GB pooled storage, calendar, and documentsUnder 300 customers who want the office suite
Microsoft 365 Business Basic seat$7.00 per user per month paid yearly, 1 to 300 usersOne licensed user per customer in your tenantExchange mail, Outlook on the web, Teams, and web Office appsUnder 300 customers who already live in Microsoft tools
Exchange Online Plan 1 seat$4.00 per user per month paid yearlyOne licensed user per customer in your tenantA 50 GB mailbox with calendar and contactsMail-only seats where Microsoft hosting is required
Nylas Agent AccountsPro: $49 a month for 50, then $0.40 per extraOne POST to /v3/connect/custom returns a grant_idA hosted mailbox and calendar with IMAP accessProducts already built on the Nylas API
AgentMailDeveloper $20 for 10, Startup $200 for 150, extras $2 eachOne inboxes.create call inside the signup request, safe to retryAn address, threads, webhooks, and a pod per tenantCustomers or their agents that send and receive as themselves

Read the table by column. A catch-all is free and gives the customer nothing but a string. Seats are real mailboxes priced like office software, because they are office software. Inbox APIs are real mailboxes priced like infrastructure. The right row depends less on price than on whether the customer ever sends.

Suppose 250 customers sign up in launch week. On Business Starter seats that's $1,750 a month, and a new user can wait up to 24 hours for Google services to work. On Exchange Online Plan 1 it's $1,000 a month. On Nylas Pro it's roughly $129 a month. On AgentMail Startup it's roughly $400 a month, because 100 of those inboxes sit past the 150 the plan includes. The catch-all is still free. It also still can't send a single reply as the customer.

When is a catch-all subdomain plus a database row enough

A catch-all subdomain plus a database row is enough when every message flows in and none flows out. Google Workspace catch-all routing sends mail for incorrect or non-existent addresses at your domain into one mailbox you choose. Your app hands each customer a string like acme@in.yourapp.com and stores it on their row. Each message is then routed by its To header.

That's a fine design for intake. Receipts forwarded into an expense tool, leads parsed into a CRM, alerts turned into tickets. Nobody expects a reply from acme@in.yourapp.com, so it doesn't need an identity.

It stops working the day a customer has to answer. Every reply leaves from the shared mailbox. Every customer shares one sender reputation. There are no threads per customer to read back, so your app rebuilds conversation history from raw messages.

A catch-all also accepts every guessed address. Spam aimed at random local parts lands in the same mailbox your parser reads, and your code has to tell a real customer address from an invented one. The longer argument for agents is in catch-all vs an inbox per agent.

What a paid seat per customer costs at signup

A seat per customer turns every signup into a monthly license. At the list prices for Google Workspace Business Starter and Microsoft 365 Business Basic, 300 customers cost $2,100 a month before anyone sends an email. Exchange Online Plan 1 brings the same 300 down to $1,200 a month, for mail alone.

Then you hit the cap. Google sells Starter, Standard, and Plus for at most 300 users. Microsoft lists Business Basic for 1 to 300. Customer 301 pushes a Google tenant to Enterprise, which has no user cap and no list price on the pricing page. That's a sales call in the middle of your growth curve.

Speed is the second cost. Google says a new user can take up to 24 hours before Google services are available to them. The Directory API creates at most 10 users per domain per second. Each user it creates is a license charge on your account. A launch-day spike of signups queues behind that limit, then waits on the 24 hours.

Sending is the one place seats are generous. A paid Gmail account sends up to 2,000 messages a day, and a trial account sends 500. For one customer's correspondence, that ceiling rarely matters.

Seats fit when the customer is a person who wants an office suite. They get Gmail or Outlook, a calendar, documents, and video calls. You get an admin console, password resets, and a license to reclaim every time someone churns. Nobody on your team planned to run an IT department for strangers. It's an office suite with a mailbox attached, priced like one.

How do you create a mailbox per customer in the signup request

You create a mailbox per customer in the signup request by calling an inbox API from the handler. It sits next to the insert that creates the user row. Two products are built for it.

Nylas Agent Accounts. A hosted mailbox and calendar from one POST to /v3/connect/custom. The call returns a grant_id that works with every other Nylas endpoint. IMAP, the standard protocol mail clients use to read a mailbox, is included. On price per extra mailbox, Nylas is lower than AgentMail, as the table shows. It fits teams that already use the Nylas API for calendar or contact sync.

AgentMail. The inbox is the API object. One create call takes a username, a verified domain, a display name, and a client_id, and it returns the new address. Custom domains start on the Developer plan. A subdomain like mail.yourapp.com works once it's verified. Here is the call inside a signup handler.

# pip install agentmail==2.0.14
from agentmail import AgentMail
from agentmail.inboxes import CreateInboxRequest

client = AgentMail(api_key="am_live_your_key")

def provision_inbox(user_id: str, handle: str, name: str):
    # client_id is derived from your user id, so a retry returns this same inbox
    return client.inboxes.create(
        request=CreateInboxRequest(
            username=handle,
            domain="mail.yourapp.com",
            display_name=name,
            client_id=f"inbox-user-{user_id}",
        )
    )

inbox = provision_inbox("4821", "acme", "Acme Support")
print(inbox.email, inbox.client_id)

# Example output:
# acme@mail.yourapp.com inbox-user-4821

Pick the username from something your system already keeps unique, like the account slug. The display name is what recipients see next to the address. Keep it the customer's own brand rather than yours, so a reply from acme@mail.yourapp.com reads as coming from Acme.

B2B products often need more than one mailbox per customer. A support agent, a billing agent, and a sales agent for the same company belong together. They also belong apart from every other company. Pods do that: one pod per customer, and every inbox and domain inside it is isolated from other pods.

# pip install agentmail==2.0.14
from agentmail import AgentMail

client = AgentMail(api_key="am_live_your_key")

# One pod per customer company, keyed to your tenant id
pod = client.pods.create(name="Acme Inc", client_id="pod-tenant-77")

billing = client.pods.inboxes.create(
    pod.pod_id,
    username="acme-billing",
    domain="mail.yourapp.com",
    display_name="Acme Billing",
    client_id="inbox-tenant-77-billing",
)
print(pod.pod_id, billing.email)

# Example output:
# pod_8f2c41 acme-billing@mail.yourapp.com

The pattern for many tenants is in pods for multi-tenant email. Running a domain per customer is covered in custom domains for thousands of agent inboxes. If you're still deciding whether you need a mailbox at all, read email API vs inbox API.

What happens when signup retries or fails halfway

Signup handlers run twice more often than anyone plans for. A load balancer times out and the client retries. A user double-clicks. A job queue redelivers after a worker restart. An idempotency key is a value that makes a repeated request return the first result instead of acting twice. Any create call without one leaves deduplication to you. Every duplicate is a mailbox you pay for and a customer with two addresses.

AgentMail makes every create call idempotent through client_id. The first call with a given value creates the inbox and stores it against that value. Any later call with the same value creates nothing. It returns the original inbox with a 200. Derive the value from your own user id, never from a random string generated inside the retry loop. Keep @ out of it.

# pip install agentmail==2.0.14
from agentmail import AgentMail
from agentmail.inboxes import CreateInboxRequest

client = AgentMail(api_key="am_live_your_key")

request = CreateInboxRequest(username="acme", client_id="inbox-user-4821")

first = client.inboxes.create(request=request)
retry = client.inboxes.create(request=request)  # no second inbox is created
print(first.inbox_id == retry.inbox_id)

# Example output:
# True

Half-finished signups need an order of operations. Write the user row first with a pending status. Create the inbox with the client_id from that row's id. Then store the address and mark the row active. Suppose the process dies after the inbox exists but before the row is updated. The next attempt calls create again and gets the same inbox back. If it dies before the create call, there's nothing to clean up.

Use one client_id per resource. The pod and the inbox for the same customer get different values, like pod-tenant-77 and inbox-tenant-77-billing. A value used to create an inbox should never be reused to create a webhook or a pod.

Churn is the other half of the lifecycle. Delete the inbox when the customer leaves, because a dormant inbox still counts toward your plan. Deleting an inbox also deletes its messages and threads. Export anything you're required to keep first. A pod can't be deleted while it still holds inboxes or domains, so remove those first and the pod last.

# pip install agentmail==2.0.14
from agentmail import AgentMail

client = AgentMail(api_key="am_live_your_key")

# Deletes the inbox along with its messages and threads
client.inboxes.delete(inbox_id="acme@mail.yourapp.com")
print("deleted")

# Example output:
# deleted

Each way has a customer it fits. Catch-all routing fits inbound-only intake on a domain you already run. Google Workspace and Microsoft 365 seats fit a few hundred customers who want an office suite, and Exchange Online Plan 1 fits mail-only seats where Microsoft hosting is required. Nylas Agent Accounts fits products already built on the Nylas API. AgentMail fits products where every customer, or every customer's agent, sends and receives as its own address from the moment it signs up. More engineering posts are on the AgentMail blog.

What AgentMail does not do

AgentMail does not offer catch-all routing, so an inbound-only intake address belongs on your existing mail provider. Inbox count is a plan limit. A free-signup product with many dormant customers pays for every inbox it keeps, and creating the inbox on first use instead of at signup keeps that bill to active customers. It is not an office suite either, so customers who want documents, spreadsheets, and video meetings with their mailbox are better served by a seat.

AgentMail is the first email provider built for AI agents. Create an inbox in one API call, send and receive email, no OAuth and no domain verification. Free tier, no credit card required.

FAQ

Ready to build? Start integrating AgentMail into your AI agents today.

All systems onlineSOC 2 Compliant

Email Inboxes for AI Agents

support@agentmail.cc

Subscribe to our weekly newsletter.

© 2026 AgentMail, Inc. All rights reserved.

Privacy PolicyTerms of ServiceCopyright PolicySOC 2Subprocessors