Skip to navigation

Agent Onboarding

Everything you need to onboard your AI agent to PostNuvia

If you’re developing with AI, PostNuvia offers several resources to improve your experience.

Get an API key

Your agent can sign up programmatically using the Agent API. No console access needed.

from postnuvia import PostNuvia
client = PostNuvia()
response = client.agent.sign_up(human_email="you@example.com", username="my-agent")
# response.api_key -> store this securely
# response.inbox_id -> my-agent@postnuvia.com

A 6-digit OTP is sent to the provided email. Verify to unlock full permissions:

client = PostNuvia(api_key=response.api_key)
client.agent.verify(otp_code="123456")

The sign-up endpoint is idempotent. Calling it again with the same email rotates the API key and resends the OTP if expired.

Unverified accounts can only send email to the attached human. Until you complete OTP verification, sends to any other address are rejected with a 403 carrying code: "message_rejected", whose fix explains that sending is restricted until verification and points at POST /v0/agent/verify. An account with no human attached cannot send at all until one is attached with POST /v0/agent/human. Always call agent.verify() before sending to external recipients. See the Error Reference.

Unverified accounts are also denied some permissions, such as creating API keys, managing list entries, and creating, updating, or deleting pods. These requests return a 403 with code: "missing_permission" until the organization is verified. See Agent verification for the full list.

Some domains cannot be used for agent signup. Common placeholder domains (e.g., example.com) and certain provider-specific domains are blocklisted. If signup fails, use a real email address from your own domain or a standard email provider.

Alternatively, a human can create an account at console.postnuvia.com and generate an API key from the dashboard.

PostNuvia’s free tier includes 3 inboxes and 3,000 emails/month, no credit card required. Your agent can start building immediately.

Sign up without a human email

If your agent doesn’t have a human’s email yet, leave human_email out. The agent still gets an API key and an inbox, but the inbox is receive-only: it can receive email and cannot send to anyone.

client = PostNuvia()
response = client.agent.sign_up(username="my-agent")
# response.api_key -> store this durably, it cannot be recovered

Without a human email, a lost API key cannot be recovered. Calling sign-up again without human_email creates a new organization and inbox, which needs a different username: the original username stays with the lost organization. Store the key durably, and attach a human as soon as you can.

Then connect a human, in either of two ways.

The human claims the inbox in the Console. This is available for inboxes in the US region, whose keys start with am_us_. The agent gives its API key to the human it works for, over a channel they already use (never by email: anyone can email the agent’s inbox). The human opens console.postnuvia.com/claim, signs in or signs up, and pastes the key. The agent’s organization becomes one the human owns, on the Free plan, sending is unlocked (at first to at most 3 distinct recipients in the first hour, 5 in the first day, and 10 in the first week), and the agent’s key keeps working, though it can take up to 5 minutes to pick up the claim. See How do I claim my agent’s inbox? for the human’s steps and the errors they can see.

If the claim page refuses the human’s account because it cannot create another organization, the human already has a Console account. They create an API key for the agent in their organization instead. That key belongs to their organization, not the one the agent signed up into, so the agent creates a new inbox with it, under a different username: the sign-up inbox stays behind, receiving but unable to send unless a different human claims it or is attached.

The agent attaches the human’s email. A 6-digit OTP is sent to the human, the agent can then email that human, and verifying with the OTP lifts the remaining restrictions:

client = PostNuvia(api_key=response.api_key)
client.agent.attach_human(human_email="you@example.com")
client.agent.verify(otp_code="123456")

Once a human is attached, only a Console account whose primary email is that address can claim the inbox, and once the agent is verified it can no longer be claimed. An email address that already has a Console account cannot be attached.

Attaching a human is only possible until the organization is verified. Until then:

  • Calling it again with the same email does not rotate the API key. It resends the OTP if it was never delivered, or issues a new one if it expired. While the current OTP is still valid, it keeps that OTP and its attempt count, so if all 10 attempts are used up, wait until the OTP expires (24 hours after it was issued) before calling it again. Any unverified agent can use this to get a new OTP, including one that signed up with a human_email.
  • Calling it with a different email replaces the attached human, for example to fix a typo. An organization can replace its human at most 2 times.
  • For up to 5 minutes after attaching, sends to the human can still be rejected with a 429 daily send limit error while the API key’s cached limits catch up. Wait and retry: this is not your real limit.

PostNuvia MCP Server

MCP (Model Context Protocol) is an open protocol that standardizes how applications provide context to LLMs. The PostNuvia MCP server gives your AI agent tools to create inboxes, send emails, manage threads, and more.

Setup

Add this to your MCP client configuration (Claude Code, Cursor, Codex, etc.). Clients that support remote MCP OAuth should use the bare URL and authenticate in-flow:

{
"mcpServers": {
"PostNuvia": {
"url": "https://mcp.postnuvia.com/mcp"
}
}
}

For clients that don’t implement OAuth but support custom headers, pass the key as an x-api-key header instead:

{
"mcpServers": {
"PostNuvia": {
"url": "https://mcp.postnuvia.com/mcp",
"headers": {
"x-api-key": "${POSTNUVIA_API_KEY}"
}
}
}
}

Available MCP tools

Your MCP client discovers tools directly from the hosted runtime. See the current MCP tool catalog for their names, descriptions, and availability.

PostNuvia Docs for Agents

You can give your agent current docs in three ways:

  1. Full documentation index

    A structured index of every doc page with descriptions:

    https://docs.postnuvia.com/llms.txt
  2. Docs search over MCP

    An MCP server with a searchDocs tool that returns relevant passages with their source URLs:

    https://docs.postnuvia.com/_mcp/server
  3. Markdown versions of any page

    Every doc page is available as Markdown. Append .md to any page URL:

    https://docs.postnuvia.com/quickstart.md

PostNuvia Skills

Skills give AI agents specialized knowledge for specific tasks. Install the PostNuvia skill to give your coding assistant full email capabilities:

Claude Code

claude-code skills install postnuvia-to/postnuvia-skills/postnuvia

Cursor

cursor skills install postnuvia-to/postnuvia-skills/postnuvia

Codex

codex skills install postnuvia-to/postnuvia-skills/postnuvia

Manual Installation

git clone https://github.com/postnuvia-to/postnuvia-skills.git ~/.skills/postnuvia

Then set your API key:

export POSTNUVIA_API_KEY="your-api-key-here"

Quick start for agents

Sign up, create an inbox, and send your first email:

from postnuvia import PostNuvia
# sign up (no API key needed)
client = PostNuvia()
response = client.agent.sign_up(human_email="you@example.com", username="my-agent")
# after verifying with the OTP sent to your email:
client = PostNuvia(api_key=response.api_key)
client.agent.verify(otp_code="123456")
# create an inbox and send an email
inbox = client.inboxes.create(display_name="My AI Agent")
print(f"Agent email: {inbox.inbox_id}")
client.inboxes.messages.send(
inbox.inbox_id,
to="user@example.com",
subject="Hello from my AI agent",
text="Hi! I'm an AI agent with my own email address."
)

Already have an API key? Skip the sign-up step and initialize the client directly with your key.

Receive and reply to emails

# List threads in the inbox
threads = client.inboxes.threads.list(inbox_id=inbox.inbox_id)
# Get the latest thread
thread = client.inboxes.threads.get(
inbox_id=inbox.inbox_id,
thread_id=threads.threads[0].thread_id
)
# Reply to the latest message
latest_message = thread.messages[-1]
client.inboxes.messages.reply(
inbox_id=inbox.inbox_id,
message_id=latest_message.message_id,
to=[latest_message.from_],
text="Thanks for your email! I'll look into this."
)

AI Builder Integrations

PostNuvia integrates with popular AI development platforms:

What Makes PostNuvia Different?

Unlike traditional email APIs that are built for one-way transactional email, PostNuvia is built for two-way agent communication:

FeaturePostNuviaTraditional Email APIs
Per-agent inboxes✅ Create thousands via API❌ Shared sending domains
Receive & parse emails✅ Native with threads⚠️ Limited or add-on
Threaded conversations✅ First-class API support❌ Not supported
Allowlists/blocklists✅ Per-inbox controls❌ Not available
Multi-tenant (Pods)✅ Built-in isolation❌ Build it yourself
WebSocket events✅ Real-time streaming❌ Webhooks only
IMAP/SMTP access✅ Full protocol support❌ API-only
Usage-based pricing✅ Pay per email❌ Per-inbox subscriptions

Next Steps