Cosmoner Docs
API Reference

Email

Manage SMTP email sending, DNS verification, send limits, and credentials.

Manage SMTP email sending for project domains.

List Email Domains

GET /v1/projects/:projectId/email

Returns email domain records with credentials (passwords excluded) and computed DNS records (DKIM, SPF, DMARC).

Auth: Required (member, scope: email:read)


Get Email Domain

GET /v1/projects/:projectId/email/:emailDomainId

Returns a single email domain in the same shape as one entry of the list response — credentials (passwords excluded) and computed DNS records.

Auth: Required (member, scope: email:read)

Errors: 404 NOT_FOUND when the domain does not exist in this organization.


Enable Email for Domain

POST /v1/projects/:projectId/email

Enables email sending for a domain. Generates a 2048-bit RSA DKIM key pair and returns DNS records to add.

Auth: Required (member, scope: email:write)

Response includes DNS records:

{
  "success": true,
  "data": {
    "dnsRecords": [
      { "type": "TXT", "name": "datablock._domainkey", "value": "v=DKIM1; k=rsa; p=..." },
      { "type": "TXT", "name": "@", "value": "v=spf1 include:..." },
      { "type": "TXT", "name": "_dmarc", "value": "v=DMARC1; p=none; ..." }
    ]
  }
}

Enable Email for External Domain

POST /v1/projects/:projectId/email/external

Adds an externally managed domain for email sending. Creates the domain record, generates DKIM keys, registers the domain identity in SES, and returns the DNS records the user must add at their own DNS provider.

The domain is added to the project as PENDING: nothing has proven you control it yet, so it cannot be used elsewhere in the project — apps only route traffic to an ACTIVE domain. Publishing the DNS records below and verifying them moves it to ACTIVE.

Auth: Required (member, scope: email:write)

Request Body:

{
  "domainName": "example.com"
}

Response (201) includes DNS records:

{
  "success": true,
  "data": {
    "id": "...",
    "status": "DNS_PENDING",
    "dnsRecords": [
      { "type": "TXT", "name": "cosmoner1._domainkey.example.com", "value": "v=DKIM1; k=rsa; p=...", "purpose": "DKIM", "description": "..." },
      { "type": "TXT", "name": "example.com", "value": "v=spf1 include:cosmoner.com ~all", "purpose": "SPF", "description": "..." },
      { "type": "TXT", "name": "_dmarc.example.com", "value": "v=DMARC1; p=quarantine; ...", "purpose": "DMARC", "description": "..." }
    ]
  }
}

Errors: 502 SES_IDENTITY_CREATE_FAILED when the domain cannot be registered with the email provider. The domain record is rolled back, so the request can simply be retried.


Remove Email Domain

DELETE /v1/projects/:projectId/email/:emailDomainId

Turns off email sending for a domain: removes its SMTP credentials, the email domain record, and the sending identity at the email provider.

The domain itself stays in the project — including external domains, which may have apps bound to them. Use DELETE /v1/projects/:projectId/domains/:domainId to remove the domain.

The email provider identity is removed first. If that fails, the domain record is kept so the delete can be retried, and the response is 502 SES_IDENTITY_DELETE_FAILED with the provider's reason in the message.

Auth: Required (member, scope: email:write)


Verify DNS

POST /v1/projects/:projectId/email/:emailDomainId/verify

Re-checks DNS propagation for each required record (DKIM, SPF, DMARC). If all records resolve correctly, the status is updated to ACTIVE and a metered SMTP price is attached to the subscription. If any record hasn't propagated yet, the status stays DNS_PENDING — this is a normal state, not an error, so the response is still 200.

Publishing these records under the domain also proves you control its DNS, so an external domain still awaiting ownership verification becomes ACTIVE in the project at the same time — there is no second challenge record to publish.

Auth: Required (member, scope: email:write)

Response includes a per-record verification result:

{
  "success": true,
  "data": {
    "status": "DNS_PENDING",
    "verifiedAt": null,
    "records": [
      { "type": "TXT", "name": "cosmoner1._domainkey.example.com", "value": "v=DKIM1; k=rsa; p=...", "purpose": "DKIM", "description": "...", "verified": false, "error": "Expected value not found at this record" },
      { "type": "TXT", "name": "example.com", "value": "v=spf1 include:cosmoner.com ~all", "purpose": "SPF", "description": "...", "verified": true, "error": null },
      { "type": "TXT", "name": "_dmarc.example.com", "value": "v=DMARC1; p=quarantine; ...", "purpose": "DMARC", "description": "...", "verified": false, "error": "No TXT record found at this name" }
    ]
  }
}

Plans and Send Limits

Every project is on an email plan. A plan sets three ceilings: a monthly quota, and rolling daily and hourly limits that act as burst guards.

PlanPricePer monthPer dayPer hour
FreePer email1,00010050
Pro$20/month50,0005,0001,000
Scale$60/month200,00020,0004,000
Business$150/month750,00075,00015,000

The Free plan is pay-as-you-go: you are billed $1.00 per 1,000 emails within its allowances. Paid plans are a flat monthly price with the quota included and no per-email charge.

Exceeding any ceiling pauses sending. Hourly and daily pauses lift on their own once the rolling window drains. A spent monthly quota holds until the next period begins — or until you upgrade, which starts a fresh allowance immediately. The biller is emailed whenever sending is paused.

Get Send Limits

GET /v1/projects/:projectId/email/limits

Returns the current plan, its three allowances, usage in each window, the start of the current quota period, pause state with the reason, and any pending limit increase request.

Auth: Required (member)

{
  "success": true,
  "data": {
    "plan": "PRO",
    "monthlyQuota": 50000,
    "dailyLimit": 5000,
    "hourlyLimit": 1000,
    "sentThisMonth": 12480,
    "sentLastDay": 940,
    "sentLastHour": 61,
    "periodStart": "2026-07-14T09:12:00.000Z",
    "pausedUntil": null,
    "pauseReason": null,
    "pendingRequest": null
  }
}

Change Plan

POST /v1/projects/:projectId/email/plan

Switches the project's email plan. The change takes effect immediately and starts a fresh monthly quota period, so upgrading also lifts a pause caused by a spent quota. A pause from the hourly or daily burst guard is not lifted.

Auth: Required (member, non-viewer, scope: email:write)

{
  "plan": "PRO"
}

Request Limit Increase

POST /v1/projects/:projectId/email/limit-increase

Requests a higher hourly burst limit than your plan's, without changing plans. Notifies the platform team for review.

Auth: Required (member, scope: email:write)


Create SMTP Credential

POST /v1/projects/:projectId/email/:emailDomainId/credentials

Creates an SMTP credential (label + from address). The plain-text password is returned once and cannot be retrieved again.

Auth: Required (member, scope: email:write)

Request Body:

{
  "label": "Transactional",
  "fromAddress": "noreply"
}

Response (201):

{
  "success": true,
  "data": {
    "id": "...",
    "label": "Transactional",
    "fromAddress": "[email protected]",
    "password": "generated-password"
  }
}

Delete SMTP Credential

DELETE /v1/projects/:projectId/email/:emailDomainId/credentials/:credentialId

Auth: Required (member, scope: email:write)

On this page