ChatGPT supports MCP connectors, and anything that does not can use the plain REST API with the same capabilities and the same key. If you are building on the OpenAI Agents SDK, see that page instead.
Add the connector
In ChatGPT, add MCPmailer as a custom connector and sign in.
Or take the REST route
Anything that cannot hold a connector uses the same key over HTTP.
Create a mailbox
One per agent, so its address and its key are the same identity.
Send your first message
The curl on the right works as soon as the key exists.
curl https://mcpmailer.com/v1/messages \
-H "Authorization: Bearer mmk_live_..." \
-d '{"to": ["anna@customer.com"], "subject": "Hello", "body": "Written by an agent."}'The REST API covers what the MCP server covers, with the same key. Useful for the parts of your stack that will never speak MCP.
# Newest unread messages
curl "https://mcpmailer.com/v1/messages?unread_only=true&limit=5" \
-H "Authorization: Bearer $MCPMAILER_KEY"
# The whole conversation, oldest first
curl "https://mcpmailer.com/v1/threads/$THREAD_ID" \
-H "Authorization: Bearer $MCPMAILER_KEY"
# Reply, keeping everyone on the thread
curl "https://mcpmailer.com/v1/messages/$MESSAGE_ID/reply-all" \
-H "Authorization: Bearer $MCPMAILER_KEY" \
-H "Content-Type: application/json" \
-d '{"body": "Your refund went out this morning."}'A reply carries the threading headers and is not counted as first contact. Composing a fresh message to someone who just wrote to you is a cold send, and it is quota-limited and watched.
Write the body as markdown. We render the text and HTML parts, so you are not hand-building a MIME message.
A refused send comes back as rejected with a reason, and a quota rejection carries the time it resets. Read the reason rather than retrying blindly.
The endpoint speaks Streamable HTTP MCP with 67 tools across mail, contacts, notes, and a vault, including send_email, read_message, search_inbox, and wait_for_reply. Errors are structured so ChatGPT can react: a quota rejection includes the reset time.