Skip to content

Enable email routing

POST
/domains/{domain}/email-routing/enable
curl --request POST \
--url https://api.nsin.ir/domains/example.com/email-routing/enable \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "remove_existing_mx": true, "merge_spf": true }'

Publishes the MX, SPF and DKIM records and turns routing on. Any MX records the domain already has must be removed (they would route mail elsewhere) — send remove_existing_mx: true to agree, otherwise the call answers 409 with conflict: mx and the rows. An existing SPF record is merged rather than duplicated (merge_spf: true); two SPF records answer 409 with conflict: spf_multiple. The removed MX rows are kept so a later disable can put them back.

Refused while no mail host is ready (503, code: hosts_not_ready), on external DNS (409, conflict: external_dns) and on a plan without the feature (403). Idempotent: an enabled domain answers 200 unchanged. Requires domain.settings.

domain
required
string

The domain name (for example example.com) — not a numeric id.

Example
example.com
Media type application/json
object
remove_existing_mx

Agree to remove the domain’s existing MX records (they are snapshotted for disable).

boolean
merge_spf

Agree to merge our include term into the domain’s existing SPF record.

boolean
Example generated
{
"remove_existing_mx": true,
"merge_spf": true
}

Routing enabled; the status.

Media type application/json
object
enabled
boolean
status
string
Allowed values: unconfigured ready misconfigured unlocked suspended
hosts_ready

At least one mail host has checked in recently. Enable is refused while false.

boolean
locked

The managed records are not editable.

boolean
subaddressing
boolean
suspended_reason
string
catch_all
object
enabled
boolean
action
string
Allowed values: forward drop
destination_id
integer
nullable
destination_email
string
nullable
destination_verified
boolean
dns
object
checked_at
string format: date-time
nullable
ok
boolean
records
Array<object>
object
type
string
Allowed values: MX TXT
name

Relative to the domain; @ is the apex.

string
content
string
priority
integer
ttl
integer
state

extra is a foreign MX or a second SPF seen publicly.

string
Allowed values: ok missing extra unchecked
record_id

The managed record’s id, when the row is ours.

integer
problems
Array<string>
conflicts

Only while unconfigured — what enable would have to remove or merge.

object
mx
Array<object>
object
id
integer
name
string
content
string
priority
integer
spf
object
record_id
integer
content
string
merged
string
spf_multiple
boolean
previous_mx_available

Enable removed MX records that disable can restore.

boolean
counts_24h
object
received

Every message except discarded bounces.

integer
forwarded
integer
dropped
integer
rejected
integer
deferred
integer
limits
object
max_rules
integer
nullable
rules_used
integer
max_forwards_per_day
integer
nullable
forwards_today
integer
dkim

Always present. Before Email Routing is enabled the selector and name are already known (they come from platform settings) and txt is empty, because the key is generated at enable time.

object
selector
string
name

The TXT name relative to the domain.

string
txt
string
platform
object
mx_hosts
Array<object>
object
host
string
priority
integer
spf_include
string
forward_domain
string
Example
{
"status": "unconfigured",
"catch_all": {
"action": "forward"
},
"dns": {
"records": [
{
"type": "MX",
"state": "ok"
}
]
}
}

Missing, malformed, revoked or expired API key — or the owning account is inactive.

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Examples
Example invalidKey
{
"error": "invalid API key"
}

The key is read-only, your role lacks the permission, the domain has no active plan (402-class message with upgrade_url), the plan does not include Email Routing, or the plan’s max_email_rules is reached — the body then carries max_rules and upgrade_url.

Media type application/json
object
error
required

Human-readable description of what went wrong.

string
max_rules
integer
nullable
upgrade_url
string
Examples
Example atCap
{
"error": "You have reached the maximum number of email addresses for this domain's plan. Upgrade the plan for this domain to add more.",
"max_rules": 50,
"upgrade_url": "/plans"
}

No such domain, or it is not visible to this account. Domains you cannot access are reported as not found rather than forbidden.

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Example
{
"error": "read-only API key"
}

Something in the zone has to change first. conflict says what: mx (existing MX records, listed under records — re-send with remove_existing_mx: true), spf (an existing SPF record, with the merged preview — re-send with merge_spf: true), spf_multiple (two SPF records; keep one), external_dns (the domain does not use NSIN DNS) or domain_status (the domain is disabled).

Media type application/json
object
error
required

Human-readable description of what went wrong.

string
conflict

What conflicts (mx, spf, spf_multiple, external_dns, domain_status, not_enabled, local_part, destination).

string
records

For mx / spf_multiple — the rows in the way.

Array<object>
object
id
integer
name
string
content
string
priority
integer
record_id

For spf — the existing SPF row.

integer
content

For spf — its current value.

string
merged

For spf — what it becomes with our include merged in.

string
destination

For destination — the existing row.

object
id
integer
email
string
verified
boolean
verified_at
string format: date-time
nullable
created_at
string format: date-time
last_sent_at

When the last verification mail went out.

string format: date-time
nullable
resend_cooldown_seconds

Seconds until another verification mail may be sent; 0 when allowed now.

integer
in_use

Rules and catch-alls pointing at it.

integer
last_failure

The receiving server’s last refusal, when forwards to it are bouncing.

string
last_failure_at
string format: date-time
nullable
Examples
Example existingMX
{
"error": "this domain already has MX records; enabling email routing replaces them",
"conflict": "mx",
"records": [
{
"id": 12,
"name": "@",
"content": "mail.example.ir.",
"priority": 10
}
]
}

The key exceeded its request budget (300 requests per minute by default).

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Examples
Example limited
{
"error": "rate limit exceeded"
}

The nameservers could not be updated; nothing was changed. Retry in a moment.

Media type application/json

The single error shape used by every endpoint.

object
error
required

Human-readable description of what went wrong.

string
Example
{
"error": "read-only API key"
}

No mail host has checked in recently (code: hosts_not_ready), or the platform is missing its key-encryption key (code: kek_unset).

Media type application/json
object
error
required

Human-readable description of what went wrong.

string
code

Machine-readable reason (hosts_not_ready, kek_unset, loop, no_mx, unverified).

string
field

The offending body field, when one applies.

string
Example
{
"error": "read-only API key"
}