# Mail Service Now > Email Services & API for Private Clients. Hosted mailboxes and forwarding on a domain you own, a sending API, and an MCP server. Mail Service Now sends mail. Partner products stay their own products. The shared inbox@mailservicenow.com mailbox stays with Mail Service Now. Do not invent a key. Do not commit a key. ## How an agent requests access No API key is required for these two calls. 1. Call MCP tool request_access, or POST /api/requestAccess, with contactEmail, domain, agentName, and intendedUse. 2. Wait for the email at contactEmail. The code is 8 digits and expires in 24 hours. The HTTP response never includes the code. 3. Call MCP tool verify_access_request, or POST /api/verifyAccessRequest, with requestId and code. 4. A ready response includes access "standard" and apiKey (msn_…) once. A setting_up response has no key and says: We're setting up your account. We'll email you when it's ready. An account already on file is left unchanged. If it can already send, the response is another standard key once. If setup is still in progress, the response is setting_up. Otherwise status is unavailable, the message is "This request could not be completed.", and there is no key. 5. Store the key in a secret store. It is shown once. Send it as Authorization: Bearer msn_… or X-Api-Key: msn_…. If X-Api-Key is present, that header is the only credential checked. A standard key can send, manage domains and mailboxes, register webhooks, and call the customer MCP tools. Default quotas are 100 credits, 5 sends per minute, and 50 per UTC day. Admin routes and admin MCP tools return 403. Rate limit on request_access: 5 per hour per IP and 3 per hour per email. 429 includes retryAfter seconds. Wrong code is 401. Five failures lock the code (401). Expired code is 410. Unknown requestId is 404. A completed request verified again is 409. Invalid input is 400. ## Agent onboarding sequence 1. Request access (request_access). 2. Verify the emailed code (verify_access_request). 3. If the result is pending, poll get_request_status (e.g. every 20 minutes). Do NOT resend the same requestId. 4. Once the account is ready, get the key with a fresh request_access plus verify_access_request. The key is shown once. "Do NOT resend the same requestId" applies only while the result is pending. 5. Add and verify the domain, create a hosted mailbox, and check get_account_status until canSend is true. 6. Send, always setting from. An account can hold one domain. POST /api/addDomain and MCP add_domain accept the account key. The same normalized name (lowercase, trimmed, trailing dot removed) that is already on the account returns created false and does not change mailboxes or DNS. A second domain, including a subdomain, is 409 one_domain_per_account. After the only domain is removed, one domain can be added again. Accounts that already hold more than one domain keep their mailboxes and DNS. A new add on those accounts is refused. 409 already complete: that request was already finished. Do not retry it. Once the account is ready, get the key with a fresh request_access plus verify_access_request. The key is shown once. "Do NOT resend the same requestId" applies only while the result is pending. Domain already claimed: the domain belongs to another account. Request a separate account for it only if you own it, or contact support. Each domain needs its own account. ## Check if my account is ready Keep the requestId. If verify returned setting_up, Mail Service Now emails that address when the account is ready. No key exists yet. Once the account is ready, get the key with a fresh request_access plus verify_access_request. The key is shown once. "Do NOT resend the same requestId" applies only while the result is pending. Poll get_request_status, or POST /api/getRequestStatus, with requestId and email. No API key. Poll about every 20 minutes. get_request_status allows 10 per hour per IP and 6 per hour per email, counted separately from request_access, so that poll has room for an extra check. Unknown email, a real email with the wrong requestId, and an unknown requestId all return HTTP 200 { "ok": true, "status": "pending", "ready": false }. A match also includes requestId. status is ready, setting_up, pending, or unavailable. ready is true only when the account is ready. A ready body is { "ok": true, "status": "ready", "ready": true, "requestId": "ar_…" }. HTTP 429 is { "ok": false, "error": "Rate limit exceeded", "retryAfter": 3600 } whether or not the email is on file. After verify_access_request, call get_account_status or POST /api/getAccountStatus with that account's own key. It returns status, accessLevel, domain verification (mx, spf, dkim, dmarc), mailbox readiness, and canSend. When canSend is false, reason is a short next step such as "Add a fully hosted mailbox on a verified domain before sending." The shared agent key, the mail-server sync token, and another account's key are refused. The call returns only the account that owns the key. A hosted address needs a domain you own. Publish MX, SPF, DKIM, and DMARC (https://mailservicenow.com/developers/domain-setup/), then create a hosted mailbox. ## After a standard key: domain and mailboxes A ready response with apiKey does not add the domain or create mailboxes. intendedUse does not provision support@ or orders@. Full checklist: https://mailservicenow.com/docs/mailservicenow-for-ai.md#after-you-have-a-standard-key-set-up-your-domain-and-mailboxes DNS for each record, including DMARC: https://mailservicenow.com/developers/domain-setup/ 1. ready plus a standard msn_… key means you can call send and the domain routes. setting_up has no key. Domain calls then return 403 Account setup is not complete yet. Send before a hosted mailbox on a verified domain returns 403 Add a fully hosted mailbox on a verified domain before sending. 2. POST /addDomain with {"domain":"example-domain.com"} and Authorization: Bearer msn_ (or X-Api-Key), or MCP add_domain. Or Add domain in the web app. The live call checks DNS immediately. created true and status failed, before you publish MX, is expected. Read dnsInstructions.records. The same normalized name again returns created false. A second domain is 409 one_domain_per_account. A domain owned by another account is 409 domain_claimed. 3. Publish the returned records. MX mail.mailservicenow.com priority 10. One SPF TXT, copied from the dashboard (https://mailservicenow.com/developers/domain-setup/#spf). DKIM TXT at mail._domainkey, value from the response. The preferred MX must be mail.mailservicenow.com. Moving MX off Cloudflare Email Routing (route1.mx.cloudflare.net or any prior host) stops mail that host receives today. Cut over only when you mean to. Turn off Cloudflare Email Routing (or your other inbound provider) or delete its MX rows. Delete every MX row for any other provider. A row left at the same or a better priority keeps sending some of the domain's mail to that provider. 4. POST /verifyDomain with {"domainId":"example-domain.com"} (or Check DNS in the app) once DNS propagates. verified true sets status verified. transient true means retry. 5. POST /addMailbox with {"domainId":"example-domain.com","localPart":"orders","mode":"hosted"} for each address. The standard key works on this REST call and on MCP add_mailbox. A Firebase ID token also works. The shared agent key is refused with Firebase sign-in required. MCP add_domain adds the one domain. The web app can add the mailbox too. 6. Wait about 60 seconds. mtaApplied becomes true after the mail server sync. IMAP, SMTP, and webmail need a verified domain and mode hosted or both. 7. Optional: POST /webhooks, a proof send, GET /credits. An admin pending row with storedStatus code_sent, including an expired code, has decidable false and is not a mailbox hold. A decision returns 409 This request is not ready for a decision. setting_up means the account is still being finished. Once ready, do the list above. ## Copy-paste Full steps, curl, and MCP JSON-RPC, in the How an agent requests access section: https://mailservicenow.com/docs/mailservicenow-for-ai.md ```bash export MSN_API_BASE=https://us-central1-mailservicenow.cloudfunctions.net curl -sS -X POST "$MSN_API_BASE/requestAccess" \ -H "Content-Type: application/json" \ -d '{"contactEmail":"ops@example-domain.com","domain":"example-domain.com","agentName":"ExampleAgent","intendedUse":"Send a notification from an agent"}' ``` ```json {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"request_access","arguments":{"contactEmail":"ops@example-domain.com","domain":"example-domain.com","agentName":"ExampleAgent","intendedUse":"Send a notification from an agent"}}} ``` POST that JSON to https://us-central1-mailservicenow.cloudfunctions.net/mcp with Content-Type application/json and no Authorization header. Then call verify_access_request with requestId and code. ## Docs - [MailServiceNow for AI](https://mailservicenow.com/docs/mailservicenow-for-ai.md): system of record for agents - [Agent guide](https://mailservicenow.com/docs/agents.html): MCP tool reference - [OpenAPI](https://mailservicenow.com/docs/openapi.json): REST paths, including /requestAccess, /verifyAccessRequest, /getRequestStatus, and /getAccountStatus - [Developers](https://mailservicenow.com/developers/): quickstart - [Domain setup / deliverability](https://mailservicenow.com/developers/domain-setup/): MX, SPF, DKIM, DMARC, and how to check them - [Any agent product](https://mailservicenow.com/developers/any-agent/) - MCP server: https://us-central1-mailservicenow.cloudfunctions.net/mcp (version 0.7.0, protocol 2024-11-05) ## Admin requests An admin sign-in can delete access requests. MCP `operator_delete_request` takes `id` or `ids` (at most 200). REST is POST /api/admin/requestDelete with the same body. The account and its key stay as they are. Each id writes one request.delete audit row and clears that email's request rate counter. Preview duplicates first with GET /api/admin/requestDuplicates?email= or domain= (one of those is required), untick rows, then send the checked ids. The server does not delete every match on its own. A standard key, the shared agent key, and the MTA token are refused. An admin sign-in can delete a domain from the Domains page, including a verified, pending DNS, or failed row. The confirm dialog names the domain and its mailbox count. If it has any mailbox, type the domain name. Mail stored for this domain is permanently deleted and cannot be recovered. MCP `operator_remove_domain` takes `uid` or `ownerEmail`, `domainId`, and `confirm` when mailboxes exist. REST is POST /api/admin/removeDomain. `mailservicenow.com` is 409 `system_domain`. A domain whose account is gone can be deleted; the audit row sets `ownerMissing` true. The claim is released in that write when it still names that account, so another account can add the domain. A customer's own mailbox removal does not release the claim. A standard key, the shared agent key, and the MTA token are refused on that delete.