Let customers use Mail Hosting and SMTP Relay
The portal API for a customer's mail domains, mailboxes and aliases, and for sending through SMTP Relay.
These portal routes let a customer run the Mail Hosting and SMTP Relay services they bought from you: add their domains, create mailboxes and aliases, move old mail in, and send through a relay. They are the customer mail API from the Email section on a different path. A route that is /api/v1/client/mail/{service_id}/mailboxes there is /api/v1/orgs/acme/portal/mail/{service_id}/mailboxes here, with the same request body and the same answer. This page lists what differs and walks through the tasks a storefront needs most.
Before you begin
Section titled Before you begin- Get a customer token as Sign customers in to your storefront describes. Every call on this page sends it as
Authorization: Bearer $CUSTOMER_TOKEN. Only the customer who bought a mail service can use it. - Sell Mail Hosting or SMTP Relay plans in your catalogue, as Set up products and pricing explains. Customers order them like any other plan; see Take an order.
- Ask support to set your organization's mail host names before you sell mail. Your customers' mail apps connect to these names. Your settings show them in a read-only card, as Change organization settings describes, and any name you have not set falls back to one of ours.
How the portal differs from the customer API
Section titled How the portal differs from the customer API- The paths start with
/api/v1/orgs/{org_slug}/portal/mail/{service_id}or/api/v1/orgs/{org_slug}/portal/smtp-relay/{service_id}. Both accept either kind of service. A route only a relay has, such as sending, answers404 Not an SMTP Relay serviceon a Mail Hosting service under either path. service_idis the id inservice_idfromGET /portal/mail/services. It differs from theidinGET /portal/services, which the list returns asorg_service_idand which the billing routes take, such as Change a plan and Cancel a service.- Host names come from your organization.
hostnamesin the summary andsmtpin a credential's answer carry your names, with ours in place of any you have not set. The summary'sclusterblock always names our servers, andproduct_namein the service list is the name of our plan behind the service, so show your customershostnamesand the product name fromGET /portal/servicesinstead. - When a mailbox turns on two-factor authentication, the authenticator app lists it under your organization's name.
- A request for a higher hourly limit opens a conversation in your organization's support inbox. See Pass on a request for a higher limit.
- An import that signs in to Gmail or Microsoft 365 starts only when your storefront calls the API on a storefront host we serve for you. See Move old mail in.
Find the customer's mail services
Section titled Find the customer's mail servicescurl "https://api.coritan.com/api/v1/orgs/acme/portal/mail/services" \
-H "Authorization: Bearer $CUSTOMER_TOKEN"
The answer is {"items": [...], "total": n}, newest first. It leaves out cancelled and terminated services, and dedicated IP add-ons, which show in their relay's summary under dedicated_ips. Each item has these fields:
| Field | What it holds |
|---|---|
service_id |
The id every other route on this page takes. |
org_service_id |
The service's id in your organization, for the billing routes. |
module_name |
mail for Mail Hosting, smtp_relay for SMTP Relay. |
product_name |
The name of our plan behind the service. |
status |
Where the service stands, such as active or suspended. |
hostname |
The service's name. |
tenant |
The mail service itself: its id, kind (hosting or relay), status, plan limits and reputation. It is null while we set the service up. |
created_at |
When the customer ordered the service. |
Read one service's summary:
curl "https://api.coritan.com/api/v1/orgs/acme/portal/mail/4812" \
-H "Authorization: Bearer $CUSTOMER_TOKEN"
The answer is the summary that How Mail Hosting works and How SMTP Relay works describe: kind, status, the plan's limits, counts, used_bytes and hostnames, plus month_to_date_sent, limits and dedicated_ips for a relay. It also carries org_service_id. hostnames holds these fields:
| Field | What it holds |
|---|---|
smtp_host, imap_host |
The servers mail apps connect to. |
jmap_host |
Always one of ours. |
mx_hostname_base |
The mail server suffix your organization presents. The MX records a domain needs always name our servers, so take them from the domain's records. |
webmail_url |
Where a mailbox signs in to webmail. |
webmail_mode |
unset when we do not serve webmail at webmail_url. Hide your webmail link then. |
display_name, support_url, postmaster_address, user_mailbox_domain |
Values from your organization's mail settings, or null. |
Add a customer's domain
Section titled Add a customer's domaincurl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/mail/4812/domains" \
-H "Authorization: Bearer $CUSTOMER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com", "primary": true}'
domain is 3–253 characters. The answer is 201 with the domain and records, the DNS records it needs. What happens next depends on where the domain's DNS is hosted:
- When we host the domain's zone and it belongs to the customer, we publish the records and the domain proves itself at once. That covers a name the customer claimed under one of your free suffixes, as Offer free names under your domain describes, and a DNS zone they ordered from you as a service.
- Any other domain waits until its records are published. The customer publishes them at their DNS provider. A zone in your organization's own DNS account proves nothing for a customer either, so your staff publish the records there on the DNS tab, as Host DNS zones for your organization shows.
Once the records are published, check the domain:
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/mail/4812/domains/311/verify" \
-H "Authorization: Bearer $CUSTOMER_TOKEN"
The answer holds the domain with its new status, and result with each check. GET .../domains/{domain_id}/records lists the records again, and DELETE .../domains/{domain_id} removes the domain. Add a domain to Mail Hosting and Add a sending domain to SMTP Relay explain each check and each refusal, and Mail DNS records explains each record.
To hold a domain's DMARC policy at one stage, or to move it, send stage (none, quarantine or reject), pinned, or both:
curl -X PATCH "https://api.coritan.com/api/v1/orgs/acme/portal/mail/4812/domains/311/dmarc" \
-H "Authorization: Bearer $CUSTOMER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"stage": "quarantine", "pinned": true}'
How the DMARC stage ramp works explains the stages.
Create mailboxes and aliases
Section titled Create mailboxes and aliasescurl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/mail/4812/mailboxes" \
-H "Authorization: Bearer $CUSTOMER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"domain_id": 311, "local_part": "alex", "display_name": "Alex Morgan"}'
local_part is the part before the @, 1–64 characters. password is optional, 12–128 characters, and quota_bytes is optional, at least 100 MiB. Leave out password and we generate one: the answer is 201 with the mailbox, and password holds the generated password this once. Show it to the customer then, because we cannot show it again.
Every path below starts with /api/v1/orgs/acme/portal/mail/{service_id}:
| Route | Body | What it does |
|---|---|---|
GET /mailboxes |
Lists the mailboxes. | |
POST /mailboxes/{account_id}/password |
{"password": "..."}, or {} |
Sets the password you send, or generates one and returns it. A generated password also turns off two-factor authentication. Either way, every webmail session ends and app passwords keep working. |
PATCH /mailboxes/{account_id}/quota |
{"quota_bytes": 10737418240} |
Changes the mailbox's share of the plan's storage. |
PATCH /mailboxes/{account_id}/enabled |
{"enabled": false} |
Turns the mailbox off or on. |
DELETE /mailboxes/{account_id} |
Deletes the mailbox. | |
GET /aliases |
Lists the aliases. | |
POST /aliases |
{"domain_id": 311, "local_part": "sales", "targets": ["alex@example.com"]} |
Creates an address that delivers to 1–50 other addresses. |
DELETE /aliases/{account_id} |
Deletes an alias. |
Create and manage mailboxes and Forward addresses with aliases give the full fields and refusals.
Secure a mailbox
Section titled Secure a mailboxEvery path below starts with /api/v1/orgs/acme/portal/mail/{service_id}/mailboxes/{account_id}:
| Route | What it does |
|---|---|
GET /app-passwords |
Lists the mailbox's app passwords, which mail apps sign in with. |
POST /app-passwords |
Creates one. Send label, 1–64 characters. The password is in secret, shown this once. |
DELETE /app-passwords/{credential_id} |
Revokes one. |
POST /totp |
Turns on two-factor authentication and returns otpauth_uri and secret once, for the customer's authenticator app. Every webmail session ends. |
DELETE /totp |
Answers 400, because it needs the mailbox's password and takes none. To turn two-factor authentication off, send {} to POST .../password instead. |
GET /sessions |
Lists the browsers signed in to webmail as the mailbox. |
DELETE /sessions/{session_id} |
Signs one browser out. The answer is 204. |
DELETE /sessions |
Signs every browser out. The answer is 204. |
The issuer in otpauth_uri is your organization's name. Secure a mailbox explains app passwords, two-factor authentication and sessions.
Move old mail in
Section titled Move old mail inThe import routes sit under /api/v1/orgs/acme/portal/mail/{service_id}/mailboxes/{account_id}/imports and work as Move existing mail into a mailbox describes:
| Route | What it does |
|---|---|
GET .../imports |
Lists the mailbox's imports with their status and counts. It also returns the providers you can offer in presets, the sign-ins on offer in oauth, and the largest file an upload takes in max_upload_mb. |
POST .../imports |
Copies mail from another server over IMAP. Send username, password, and a preset from the list or a host. |
POST .../imports/upload |
Imports a file, sent as a multipart form with the file in file. |
POST .../imports/oauth |
Starts a sign-in to Gmail or Microsoft 365 and answers {"url": "..."}. |
POST .../imports/{import_id}/pause |
Pauses an import. resume and cancel in place of pause resume or cancel it. |
For example, to copy the mail from another server:
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/mail/4812/mailboxes/5120/imports" \
-H "Authorization: Bearer $CUSTOMER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"preset": "other", "host": "imap.example.com", "username": "alex@example.com", "password": "the-old-password"}'
For a sign-in, send provider (google or microsoft) and return_path, a path on your storefront, then send the customer's browser to url. After the sign-in, the browser comes back to that path with import=started, or import_error and the reason, in the query.
A sign-in comes back only to the host your storefront called the API on, so it works only when that host is one we serve for your organization. Called on api.coritan.com, the route answers 422 with the code oauth_origin. A storefront that calls the API there can offer the server import and the file upload, which work on any host.
Send through SMTP Relay
Section titled Send through SMTP RelaySending, SMTP credentials and send API keys need an SMTP Relay service. The paths below start with /api/v1/orgs/acme/portal/smtp-relay/{service_id}.
For an application that sends over SMTP, create a credential on one of the relay's domains:
Shellcurl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/smtp-relay/4812/credentials" \ -H "Authorization: Bearer $CUSTOMER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"domain_id": 311, "label": "billing app"}'The answer is
201with the credential: the username inaddress, thepasswordthis once, andsmtp, the host names with your organization's names in them. Send over SMTP with credentials gives the ports and the routes that rotate, turn off and revoke a credential.For an application that sends over HTTPS, create a send API key:
Shellcurl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/smtp-relay/4812/api-keys" \ -H "Authorization: Bearer $CUSTOMER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"label": "shop"}'The answer is
201, andkeyholds the key this once. The application sends with it toPOST /api/v1/mail/send, as Send email over HTTPS shows. A relay holds up to 20 keys that are not revoked.GET /api-keyslists them andDELETE /api-keys/{key_id}revokes one.To send from your storefront with the customer's token, post the message:
Shellcurl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/smtp-relay/4812/messages" \ -H "Authorization: Bearer $CUSTOMER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"from": "receipts@example.com", "to": ["alex@example.com"], "subject": "Your receipt", "text": "Thanks for your order."}'The body and the
202answer are those of the send API.
The other sending routes:
| Route | Fields | What it does |
|---|---|---|
PATCH /category |
default_category: transactional or marketing |
Sets the category a message takes when it names none. See Transactional and marketing mail. |
GET /suppressions, POST /suppressions, DELETE /suppressions/{suppression_id} |
address |
Lists, adds or removes addresses the relay does not send to. See Manage the suppression list. |
GET /webhooks, POST /webhooks, DELETE /webhooks/{webhook_id} |
url (https:// only), events |
Posts delivery events to the customer's URL, up to 10 webhooks a service. The answer to POST holds the signing secret once. See Receive delivery events with webhooks. |
GET /events |
category, recipient, hours (1–2160, default 24), limit (1–500, default 100) |
Lists message events, newest first. See Look up message events. |
GET /usage |
days (1–365, default 30) |
Returns the messages sent each day, and the month's total against the plan. |
GET /reputation |
limit (1–200, default 50) |
Returns bounce and complaint rates against our thresholds, each domain's checks, and the reports receiving servers sent. See Sending reputation and deliverability. |
Suppressions, webhooks, events, usage and reputation work on a Mail Hosting service too.
Pass on a request for a higher limit
Section titled Pass on a request for a higher limitA relay sends up to its hourly limit, which How SMTP Relay sending limits work explains. When a customer needs more, send the number they want and why:
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/portal/smtp-relay/4812/limits/increase-request" \
-H "Authorization: Bearer $CUSTOMER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"requested_per_hour": 2000, "reason": "Order receipts for a seasonal sale"}'
requested_per_hour is 10–1,000,000 and must be above the current limit. reason is up to 2,000 characters. The answer is 201 with conversation_id, current_per_hour and requested_per_hour.
The request opens a conversation from the customer in your organization's support inbox, with the relay's sending figures in its first message; Answer customer conversations shows where to find it. Only Coritan can raise a relay's limit, so contact support with the relay's service_id and the number, then answer the customer. A second request for the same relay on the same day (UTC) returns the conversation the first one opened.
Result
Section titled ResultThe customer's domains are verified, their mailboxes and aliases receive mail, and their relay sends with a credential or an API key. Your staff can open the same services from the organization API; see Run Mail Hosting for your customers.
Troubleshooting
Section titled Troubleshooting404 Mail service not found- The id is not one of this customer's mail services, or the service was terminated. Take
service_idfromGET /portal/mail/services, not theidfromGET /portal/services. 409 This mail service is still provisioning- We are still setting the service up. Try again once the service shows a
tenantinGET /portal/mail/services. 404 Not an SMTP Relay service- The route needs an SMTP Relay service, and this one is Mail Hosting. A mailbox sends through its own SMTP sign-in instead; see Connect a mail app to Mail Hosting.
409 Service is not active- The service is suspended or not ready, so it cannot add a domain or send. Suspend or unsuspend a service explains suspension.
400 example.com is one of the platform's own domains, so it cannot be added to this service- The name is in one of our own zones. Use a domain the customer owns, or a name they claimed under one of your free suffixes.
409 The DMARC record for example.com in your DNS was not added by us. Change its p= value in the zone instead.- The zone already holds a DMARC record that someone wrote by hand. Change its policy in the zone.
503 The zone is busy with another change. Check the domain again in a minute.- Another change to the domain's zone was being saved. Check the domain again.
400 Turning two-factor login off needs the mailbox password; reset the password to clear it insteadDELETE .../totpcannot turn two-factor authentication off. Send{}toPOST .../mailboxes/{account_id}/password, and give the customer the new password.422with the codeoauth_origin- The sign-in import was called on a host we do not serve for your organization, such as
api.coritan.com. Use the server import or a file upload. 422with the codeoauth_unavailable- We cannot start a sign-in with that provider. The message says why. Use the server import instead.
429 Monthly sending allowance reached; upgrade the plan to continue- The relay has sent the messages its plan allows this month. A plan with a buffer answers
Monthly allowance and its … buffer are used upinstead. Move the customer to a larger plan, as Change a plan shows. 422 The relay may already send 500 an hourrequested_per_houris not above the relay's current limit.502with a message that startsMail engine error:- Something failed on our side. Try again, and contact support if it keeps failing.