Skip to content
nsin docs

Set up email routing in one call

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

The first-run shortcut: turns routing on (exactly as the enable endpoint does, with the same remove_existing_mx / merge_spf answers and the same refusals), adds destination_email to the domain owner’s account if it is not there yet, and creates local_part@domain forwarding to it.

The destination gets one verification mail and the address stays inactive until the link is clicked — unless the destination is the owner’s own verified account email, which is saved verified and gets no mail. Everything is checked before anything is written, and the enable runs first: a refused setup creates nothing. If a later step fails after the enable (a destination cap, a rule limit) the error body carries enabled: true. Safe to repeat: an existing destination and an existing address are kept as they are. 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
local_part
required

The part before @ of the first address, e.g. info.

string
destination_email
required

The inbox its mail is forwarded to.

string format: email
remove_existing_mx

Agree to replace the domain’s existing MX records.

boolean
merge_spf

Merge NSIN’s include into the domain’s existing SPF record.

boolean

Routing is on; the status, the address and the destination.

Media type application/json
object
status
object
enabled

Whether Email Routing is switched on for the domain.

boolean
status

unconfigured — not enabled. ready — enabled, and the zone carries the MX, SPF and DKIM records mail needs. misconfigured — enabled, but the last DNS check found a record missing or a foreign one in the way (see dns). unlocked — the managed records were unlocked for editing (POST .../email-routing/unlock). suspended — forwarding is paused by NSIN (suspended_reason says why).

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

user+tag@ addresses are delivered to the user@ rule. Set with PUT .../email-routing/settings.

boolean
suspended_reason

Why forwarding is suspended — over_cap when the plan’s daily forward allowance is used up, otherwise NSIN’s note. Empty when not suspended.

string
catch_all
object
enabled

Whether the catch-all handles mail for addresses you have not created. Off, such mail is refused as an unknown user.

boolean
action

forward to the destination, or drop (accept and discard).

string
Allowed values: forward drop
destination_id

The destination for forward.

integer | null
destination_email

That destination’s address.

string | null
destination_verified

Whether the destination has confirmed its address.

boolean
dns

The DNS records mail needs, and what the last check found.

object
checked_at

When DNS was last checked; null before the first check.

string | null format: date-time
ok

Every required record was found and nothing conflicting was.

boolean
records
Array<object>
object
type

Record type.

string
Allowed values: MX TXT
name

Relative to the domain; @ is the apex.

string
content

The value the record must have.

string
priority

MX priority.

integer
ttl

TTL in seconds.

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

Human-readable findings of the last check.

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

Messages delivered to a destination.

integer
dropped

Messages accepted and discarded by a drop rule or catch-all.

integer
rejected

Messages refused.

integer
deferred

Messages temporarily refused (the sender retries), for example over the daily forward cap.

integer
sent

Recipients that mail sent from this domain (the send endpoint) was delivered to. Present on the status counters only.

integer
limits

The plan’s Email Routing allowances and today’s use.

object
max_rules

Custom addresses the plan allows on this domain. null means unlimited.

integer | null
rules_used

Custom addresses the domain holds now.

integer
max_forwards_per_day

Messages the plan lets this domain forward per UTC day. null means unlimited.

integer | null
forwards_today

Messages forwarded so far today (UTC).

integer
sending

Whether this domain may send mail right now, and how much of today’s allowance is used.

object
plan_enabled

The plan includes sending (email_sending_enabled).

boolean
allowed

A send would be accepted now (limits aside).

boolean
reason

Why not, when allowed is false: platform_off, plan, not_enabled or suspended. Empty otherwise.

string
max_per_day

Recipients the plan lets this domain send to per UTC day. null means unlimited.

integer | null
sent_today

Recipients queued so far today (UTC)

integer
max_recipients

Recipients allowed in one message.

integer
max_text_kb

Largest message text

integer
max_subject

Longest subject

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

The DKIM selector.

string
name

The TXT name relative to the domain.

string
txt

The DKIM TXT value (v=DKIM1; …). Empty until enabled.

string
platform

NSIN’s mail platform — what the domain’s records point at.

object
mx_hosts

The MX targets published for the domain.

Array<object>
object
host
string
priority
integer
spf_include

The name the domain’s SPF record must include:.

string
forward_domain

Domain the forwarded mail’s envelope sender is rewritten to (SRS).

string
rule
object
id
integer
local_part

The part before the @, lowercased.

string
address

The full address, local_part@domain.

string
action

forward delivers to the destination; drop accepts the mail and discards it.

string
Allowed values: forward drop
destination_id

The destination mail is forwarded to. null for drop.

integer | null
destination_email

That destination’s address.

string | null
destination_verified

Whether the destination has confirmed its address; unverified destinations receive nothing.

boolean
enabled

Your switch.

boolean
active

Whether the address is live right now — enabled, routing on for the domain, and (for forward) the destination verified.

boolean
name

Optional label.

string
created_at
string format: date-time
updated_at
string format: date-time
destination
object
id
integer
email
string
verified

Whether the address was confirmed through the emailed link.

boolean
verified_at

When it was confirmed.

string | null format: date-time
created_at
string format: date-time
last_sent_at

When the last verification mail went out.

string | null format: date-time
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

When last_failure was recorded.

string | null format: date-time
Example
{
"status": {
"status": "unconfigured",
"catch_all": {
"action": "forward"
},
"dns": {
"records": [
{
"type": "MX",
"state": "ok"
}
]
}
},
"rule": {
"action": "forward"
}
}

Invalid local_part or destination_email (field names which), a destination domain with no mail server (code: no_mx), or a loop (code: loop).

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"
}

Missing, malformed, revoked or expired API key. (A valid key whose account has been deactivated gets 403 account is inactive instead.)

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 domain has no active plan. The body carries upgrade_url.

Media type application/json
object
error
required

Human-readable description of what went wrong.

string
max_rules
integer | null
upgrade_url
string
Examples
Example noPlan
{
"error": "This domain does not have an active plan. Purchase a plan for this domain before adding email addresses.",
"upgrade_url": "/plans"
}

The key is read-only, your role lacks the permission, the plan does not include Email Routing (upgrade_url), 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 | null
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

Whether the address was confirmed through the emailed link.

boolean
verified_at

When it was confirmed.

string | null format: date-time
created_at
string format: date-time
last_sent_at

When the last verification mail went out.

string | null format: date-time
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

When last_failure was recorded.

string | null format: date-time
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 per-key rate limit, a verification mail to this destination less than a minute ago (cooldownSecondsRemaining), or the account’s or your address’s hourly send cap.

Media type application/json
object
error
required

Human-readable description of what went wrong.

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

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"
}