nsindocs Search the docs⌘K
  1. Docs
  2. API reference
  3. Email Routing

Enable email routing

posthttps://api.nsin.ir/domains/{domain}/email-routing/enable

Auth: Authorization: Bearer nsin_… or X-Api-Key: nsin_… — see API keys.

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), on a domain with no active plan (402) and on a plan without the feature (403). Idempotent: an enabled domain answers 200 unchanged. Requires domain.settings.

Path parameters

  • domainstringrequiredpath

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

Request body

Content type: application/json

  • remove_existing_mxboolean

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

  • merge_spfboolean

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

Responses

200Routing enabled; the status.

Content type: application/json

  • enabledboolean

    Whether Email Routing is switched on for the domain.

  • statusstring

    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).

    One of: "unconfigured" "ready" "misconfigured" "unlocked" "suspended"
  • hosts_readyboolean

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

  • lockedboolean

    The managed records are not editable.

  • subaddressingboolean

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

  • suspended_reasonstring

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

  • catch_allEmailCatchAll
    Fields
    • enabledboolean

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

    • actionstring

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

      One of: "forward" "drop"
    • destination_idinteger | null

      The destination for forward.

    • destination_emailstring | null

      That destination's address.

    • destination_verifiedboolean

      Whether the destination has confirmed its address.

  • dnsobject

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

    Fields
    • checked_atstring | null (date-time)

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

    • okboolean

      Every required record was found and nothing conflicting was.

    • recordsarray of EmailDNSRecord
      Item fields
      • typestring

        Record type.

        One of: "MX" "TXT"
      • namestring

        Relative to the domain; @ is the apex.

      • contentstring

        The value the record must have.

      • priorityinteger

        MX priority.

      • ttlinteger

        TTL in seconds.

      • statestring

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

        One of: "ok" "missing" "extra" "unchecked"
      • record_idinteger

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

    • problemsarray of string

      Human-readable findings of the last check.

  • conflictsobject | null

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

    Fields
    • mxarray of EmailConflictRecord
      Item fields
      • idinteger
      • namestring
      • contentstring
      • priorityinteger
    • spfobject | null
      Fields
      • record_idinteger
      • contentstring
      • mergedstring
    • spf_multipleboolean
  • previous_mx_availableboolean

    Enable removed MX records that disable can restore.

  • counts_24hEmailCounts
    Fields
    • receivedinteger

      Every message except discarded bounces.

    • forwardedinteger

      Messages delivered to a destination.

    • droppedinteger

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

    • rejectedinteger

      Messages refused.

    • deferredinteger

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

    • sentinteger

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

  • limitsobject

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

    Fields
    • max_rulesinteger | null

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

    • rules_usedinteger

      Custom addresses the domain holds now.

    • max_forwards_per_dayinteger | null

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

    • forwards_todayinteger

      Messages forwarded so far today (UTC).

  • sendingEmailSendingStatus

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

    Fields
    • plan_enabledboolean

      The plan includes sending (email_sending_enabled).

    • allowedboolean

      A send would be accepted now (limits aside).

    • reasonstring

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

    • max_per_dayinteger | null

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

    • sent_todayinteger

      Recipients queued so far today (UTC)

    • max_recipientsinteger

      Recipients allowed in one message.

    • max_text_kbinteger

      Largest message text

    • max_subjectinteger

      Longest subject

  • dkimobject

    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.

    Fields
    • selectorstring

      The DKIM selector.

    • namestring

      The TXT name relative to the domain.

    • txtstring

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

  • platformEmailPlatform

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

    Fields
    • mx_hostsarray of object

      The MX targets published for the domain.

      Item fields
      • hoststring
      • priorityinteger
    • spf_includestring

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

    • forward_domainstring

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

json
{
  "enabled": true,
  "status": "unconfigured",
  "hosts_ready": true,
  "locked": true,
  "subaddressing": true,
  "suspended_reason": "string",
  "catch_all": {
    "enabled": true,
    "action": "forward",
    "destination_id": 0,
    "destination_email": "string",
    "destination_verified": true
  },
  "dns": {
    "checked_at": "2026-01-01T00:00:00Z",
    "ok": true,
    "records": [
      {
        "type": "MX",
        "name": "string",
        "content": "string",
        "priority": 0,
        "ttl": 0,
        "state": "ok",
        "record_id": 0
      }
    ],
    "problems": [
      "string"
    ]
  },
  "conflicts": {
    "mx": [
      {
        "id": 0,
        "name": "string",
        "content": "string",
        "priority": 0
      }
    ],
    "spf": {
      "record_id": 0,
      "content": "string",
      "merged": "string"
    },
    "spf_multiple": true
  },
  "previous_mx_available": true,
  "counts_24h": {
    "received": 0,
    "forwarded": 0,
    "dropped": 0,
    "rejected": 0,
    "deferred": 0,
    "sent": 0
  },
  "limits": {
    "max_rules": 0,
    "rules_used": 0,
    "max_forwards_per_day": 0,
    "forwards_today": 0
  },
  "sending": {
    "plan_enabled": true,
    "allowed": true,
    "reason": "string",
    "max_per_day": 0,
    "sent_today": 0,
    "max_recipients": 0,
    "max_text_kb": 0,
    "max_subject": 0
  },
  "dkim": {
    "selector": "string",
    "name": "string",
    "txt": "string"
  },
  "platform": {
    "mx_hosts": [
      {
        "host": "string",
        "priority": 0
      }
    ],
    "spf_include": "string",
    "forward_domain": "string"
  }
}
401Missing, malformed, revoked or expired API key. (A valid key whose account has been deactivated gets 403 account is inactive instead.)

Content type: application/json

  • errorstringrequired

    Human-readable description of what went wrong.

json
{
  "error": "invalid API key"
}
402The domain has no active plan. The body carries upgrade_url.

Content type: application/json

  • errorstringrequired

    Human-readable description of what went wrong.

  • max_rulesinteger | null
  • upgrade_urlstring
json
{
  "error": "This domain does not have an active plan. Purchase a plan for this domain before adding email addresses.",
  "upgrade_url": "/plans"
}
403The 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.

Content type: application/json

  • errorstringrequired

    Human-readable description of what went wrong.

  • max_rulesinteger | null
  • upgrade_urlstring
json
{
  "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"
}
404No such domain, or it is not visible to this account. Domains you cannot access are reported as not found rather than forbidden.

Content type: application/json

  • errorstringrequired

    Human-readable description of what went wrong.

json
{
  "error": "read-only API key"
}
409Something 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).

Content type: application/json

  • errorstringrequired

    Human-readable description of what went wrong.

  • conflictstring

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

  • recordsarray of EmailConflictRecord

    For mx / spf_multiple — the rows in the way.

    Item fields
    • idinteger
    • namestring
    • contentstring
    • priorityinteger
  • record_idinteger

    For spf — the existing SPF row.

  • contentstring

    For spf — its current value.

  • mergedstring

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

  • destinationEmailDestination
    Fields
    • idinteger
    • emailstring
    • verifiedboolean

      Whether the address was confirmed through the emailed link.

    • verified_atstring | null (date-time)

      When it was confirmed.

    • created_atstring (date-time)
    • last_sent_atstring | null (date-time)

      When the last verification mail went out.

    • resend_cooldown_secondsinteger

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

    • in_useinteger

      Rules and catch-alls pointing at it.

    • last_failurestring

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

    • last_failure_atstring | null (date-time)

      When last_failure was recorded.

json
{
  "error": "this domain already has MX records; enabling email routing replaces them",
  "conflict": "mx",
  "records": [
    {
      "id": 12,
      "name": "@",
      "content": "mail.example.ir.",
      "priority": 10
    }
  ]
}
429The key exceeded its request budget (300 requests per minute by default).

Content type: application/json

  • errorstringrequired

    Human-readable description of what went wrong.

json
{
  "error": "rate limit exceeded"
}
502The nameservers could not be updated; nothing was changed. Retry in a moment.

Content type: application/json

  • errorstringrequired

    Human-readable description of what went wrong.

json
{
  "error": "read-only API key"
}
503No mail host has checked in recently (code: hosts_not_ready), or the platform is missing its key-encryption key (code: kek_unset).

Content type: application/json

  • errorstringrequired

    Human-readable description of what went wrong.

  • codestring

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

  • fieldstring

    The offending body field, when one applies.

json
{
  "error": "string",
  "code": "string",
  "field": "string"
}

Example request

bash
curl -X POST "https://api.nsin.ir/domains/example.com/email-routing/enable" \
  -H "Authorization: Bearer $NSIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "remove_existing_mx": true,
  "merge_spf": true
}'
esc