Domains API

Manage sending domains, DNS verification, inbound receiving flags, tracking preferences, TLS mode, one-click DNS apply, and DMARC/BIMI guidance.

Base URL: https://api.ranla.ai

Auth:

Authorization: Bearer rnl_...

Create a domain

POST /domains

curl -X POST https://api.ranla.ai/domains \
  -H "Authorization: Bearer $RANLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "example.com" }'

Success response:

{
  "domain": {
    "id": "d_123",
    "name": "example.com",
    "status": "pending",
    "created_at": "2026-07-26T00:00:00.000Z",
    "verified_at": null,
    "open_tracking": false,
    "click_tracking": false,
    "tls_mode": "opportunistic"
  },
  "records": [
    {
      "type": "TXT",
      "host": "_supersendtx.example.com",
      "value": "supersendtx-verify=...",
      "purpose": "Domain ownership"
    }
  ]
}

To request inbound receiving during creation, include "inbound_enabled": true. The response will also include inbound_records (MX) and inbound_note. Full receiving flow: docs/api/inbound.md.

Expected DNS records (after create syncs with the mail server):

  • ownership TXT: _supersendtx.example.com
  • DKIM TXT: selector host such as ss-abc123._domainkey.example.com with the real public key
  • return-path CNAME: rp.example.comrp.supersendtx.com (DNS only / not proxied)
  • SPF include: include:spf.supersendtx.com
  • DMARC starter record

Ranla registers the domain on the transactional mail server at create time and replaces placeholder DKIM with the real signing records before apply/verify.

Cross-team claim conflict

If the domain is already attached to another Ranla team, the API returns 409 with claim instructions:

{
  "error": {
    "message": "Domain already belongs to another Ranla team. Remove it from the current team or contact support to claim it.",
    "code": "conflict",
    "details": {
      "reason": "domain_claim_required",
      "domain": "example.com",
      "instructions": [
        "Ask the current Ranla team admin to remove the domain if you still have access.",
        "If you own the domain but no longer control the original team, contact support and include proof of DNS control."
      ]
    }
  }
}

Get a domain

GET /domains/{id}

Returns the domain, required DNS records, inbound MX guidance, and best-effort DMARC/BIMI analysis:

{
  "domain": {
    "id": "d_123",
    "name": "example.com",
    "status": "verified",
    "created_at": "2026-07-26T00:00:00.000Z",
    "verified_at": "2026-07-26T00:05:00.000Z",
    "open_tracking": true,
    "click_tracking": false,
    "tls_mode": "enforced",
    "inbound_enabled": true,
    "inbound_status": "active",
    "inbound_error": null
  },
  "records": [],
  "inbound_records": [
    {
      "type": "MX",
      "host": "example.com",
      "priority": 10,
      "value": "mx.supersendtx.com",
      "purpose": "Inbound receiving"
    }
  ],
  "inbound_note": "Inbound receiving requires pointing this hostname's MX record to Ranla. That conflicts with any existing mailbox provider on the same hostname, so use a dedicated subdomain such as inbound.example.com when Google Workspace or Microsoft 365 should keep handling your main mailboxes.",
  "analysis": {
    "dmarc": {
      "host": "_dmarc.example.com",
      "configured": true,
      "record": "v=DMARC1; p=quarantine; rua=mailto:[email protected]",
      "policy": "quarantine",
      "rua": ["mailto:[email protected]"],
      "guidance": "DMARC is enforced. Keep report mailboxes monitored and tighten alignment as you roll out BIMI."
    },
    "bimi": {
      "host": "default._bimi.example.com",
      "configured": false,
      "record": null,
      "location": null,
      "authority": null,
      "requires_dmarc_enforcement": false,
      "guidance": "Optional: add a BIMI record at default._bimi once your branded SVG logo and optional VMC are ready."
    }
  }
}

DMARC/BIMI notes

  • DMARC is recommended for every sending domain.
  • BIMI is optional, but most mailbox providers expect DMARC enforcement (p=quarantine or p=reject) before BIMI is effective.
  • analysis is advisory. It does not block sending, and it does not replace your own deliverability monitoring.

Verify DNS

POST /domains/{id}

{ "action": "verify" }

When ownership, SPF, DKIM, and the return-path CNAME are visible in public DNS, Ranla asks the mail server to re-check delivery DNS and marks the domain verified only when mail-server DKIM and return-path statuses are OK. That step activates customer-domain DKIM signing (d=yourdomain) so DMARC can pass. Without DKIM OK, mail would fall back to the shared pool signer.

DMARC is reported back but does not currently block verification. Re-running verify on an already-verified domain re-checks mail-server DKIM (useful after DNS propagation).

Success responses include:

{
  "verified": true,
  "signing_ready": true,
  "mail_server_signing": {
    "dkim_ok": true,
    "return_path_ok": true,
    "dkim_status": "OK",
    "return_path_status": "OK"
  }
}

If public DNS looks correct but the mail server has not yet accepted the DKIM TXT or return-path CNAME, verify returns 400 with mail_server_signing.dkim_ok / return_path_ok reflecting the failure — wait for propagation and verify again.


Apply DNS

POST /domains/{id}

Cloudflare via stored dashboard token

{
  "action": "apply",
  "provider": "cloudflare"
}

Cloudflare via inline credentials

{
  "action": "apply",
  "provider": "cloudflare",
  "credentials": {
    "apiToken": "cf_...",
    "zoneId": "..."
  }
}

GoDaddy via one-time credentials

{
  "action": "apply",
  "provider": "godaddy",
  "credentials": {
    "apiKey": "gd_key",
    "apiSecret": "gd_secret"
  }
}

You can also save GoDaddy credentials under Settings → Integrations and omit credentials on apply.

Vercel via stored dashboard token

{
  "action": "apply",
  "provider": "vercel"
}

The domain must use Vercel nameservers. For team-scoped domains, connect Vercel under Settings → Integrations with an optional team id, or pass credentials.teamId / credentials.vercelTeamId inline.

Vercel via inline credentials

{
  "action": "apply",
  "provider": "vercel",
  "credentials": {
    "apiToken": "vercel_...",
    "teamId": "team_..."
  }
}

Response shape:

{
  "provider": "godaddy",
  "domain": "example.com",
  "results": [
    {
      "purpose": "SPF",
      "host": "example.com",
      "action": "merged",
      "detail": "v=spf1 include:spf.supersendtx.com ~all"
    }
  ]
}

Update tracking, inbound, and TLS preferences

PATCH /domains/{id}

curl -X PATCH https://api.ranla.ai/domains/example.com \
  -H "Authorization: Bearer $RANLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inbound_enabled": true,
    "open_tracking": true,
    "click_tracking": true,
    "tls_mode": "enforced"
  }'

Aliases openTracking, clickTracking, tlsMode, and inboundEnabled are also accepted.

Inbound enablement

  • inbound_enabled: true marks the domain for receiving.
  • Before verification, inbound stays pending.
  • After verification, Ranla activates inbound receiving and updates inbound_status to active or error.
  • See docs/api/inbound.md for received-email retrieval and email.received webhooks.

TLS modes

  • opportunistic — use TLS when the downstream mail path supports it.
  • enforced — require TLS for the downstream mail path.

Tracking and TLS preferences

open_tracking, click_tracking, and tls_mode are stored on the domain immediately when you update them.

When open or click tracking is enabled:

  1. Ranla provisions a tracking domain for your hostname.
  2. The domain detail response includes a tracking CNAME (ps.yourdomain.comtrack.supersendtx.com).
  3. Apply and verify DNS so the mail server can rewrite links and inject open pixels.
  4. tracking_dns.ok in GET /domains/{id} reports whether the CNAME is live.

If delivery-side provisioning fails, settings are still saved but tracking_error or tls_error may be returned on PATCH with a customer-facing message (not raw infrastructure errors).


SDK

import { Ranla } from '@supersend/ranla'

const client = new Ranla(process.env.RANLA_API_KEY!)

await client.domains.create({ name: 'example.com' })
await client.domains.apply('example.com')
await client.domains.apply('example.com', { provider: 'vercel' })
await client.domains.apply('example.com', {
  provider: 'godaddy',
  credentials: {
    apiKey: process.env.GODADDY_API_KEY!,
    apiSecret: process.env.GODADDY_API_SECRET!,
  },
})
await client.domains.verify('example.com')

const detail = await client.domains.get('example.com')
const updated = await client.domains.update('example.com', {
  open_tracking: true,
  tls_mode: 'enforced',
})

CLI

supersendtx domains add example.com
supersendtx domains apply example.com --provider cloudflare
supersendtx domains apply example.com --provider vercel
GODADDY_API_KEY=... GODADDY_API_SECRET=... supersendtx domains apply example.com --provider godaddy
supersendtx domains verify example.com

Delete a domain

DELETE /domains/{id}

Removes the domain from your account and revokes sending authorization for that hostname.

supersendtx domains delete example.com

If upstream teardown fails, the domain is kept and the API returns 502.


Common errors

Status Meaning
400 invalid domain, DNS not ready, missing provider credentials
401 missing or invalid auth
403 domain limit reached
404 domain not found
409 domain already exists or is already claimed
502 Domain registration, teardown, or DNS provider API failed

Full contract: openapi/supersendtx.yaml