Developers

API reference

Base URL https://transmitly.io/v1. Authenticate with a bearer API key. All mutating endpoints are tenant-scoped and audited.

Quickstart

  1. 1. Create a workspace and verify a sending domain.
  2. 2. Publish the ownership TXT, SPF, DKIM and DMARC records shown in the dashboard.
  3. 3. Click verify. ACTIVE means SPF and DKIM are aligned.
  4. 4. Create an API key with the send scope.
  5. 5. POST a message and watch delivery events in the dashboard.
send your first message
curl -X POST https://transmitly.io/v1/emails \
  -H "Authorization: Bearer $TRANSMITLY_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: welcome-8f2c" \
  -d '{
    "from": "Notify <[email protected]>",
    "to": ["[email protected]"],
    "subject": "Welcome to Acme",
    "html": "<p>Your workspace is ready.</p>",
    "stream": "transactional",
    "tags": {"flow": "onboarding"}
  }'

POST /v1/emails

Accepts a message and returns 202 only after the message, per-recipient deliveries, idempotency key and outbox event commit in one transaction.

FieldTypeNotes
fromstringMust be on a verified domain in your workspace
tostring | string[]Max 50 recipients per message
subjectstringRequired, ≤ 500 characters
html / textstringAt least one required; html ≤ 1 MiB
streamstringtransactional (default) or marketing
tagsobjectString key/value pairs, echoed in events
headersobjectAdditional headers; Message-ID is generated
202 Accepted
{"id": "msg_2f9c…", "status": "accepted", "recipients": 1}

Fetch state with GET /v1/emails/{id} orGET /v1/messages/{id}/deliveries.

Idempotency

Send an Idempotency-Key header with every message. Replaying the same key and identical body returns the original message id with"idempotent": true. Reusing a key with a different body returns 409 idempotency_conflict.

SMTP cannot guarantee exactly-once delivery. We reconcile uncertain submit outcomes before retry, but ambiguous network failures may still produce duplicates. Design receivers to be idempotent.

Domain & DNS

Each domain gets a 2048-bit RSA DKIM key pair and a verification token. Publish:

DNS records
_transmitly.mail.acme.com     TXT  "transmitly-verify=<token>"
mail.acme.com                TXT  "v=spf1 include:spf.transmitly.io ~all"
transmitly._domainkey.mail.acme.com  TXT  "v=DKIM1; k=rsa; p=<public key>"
_dmarc.mail.acme.com         TXT  "v=DMARC1; p=none; rua=mailto:[email protected]"
mail.acme.com                MX   10 inbound.transmitly.io

Status transitions: PENDING → VERIFIED → ACTIVE → DEGRADED → SUSPENDED. Ownership loss on an active domain degrades it and pauses sending pending review. Private DKIM keys never leave the control plane and are never logged or sent to a model.

Webhooks

Events: email.accepted,email.deferred,email.delivered_to_mx,email.bounced,email.complained,email.suppressed,domain.degraded,autopilot.action.

signature verification
const [ts, sig] = req.headers["transmitly-signature"]
  .split(",").map(v => v.split("=")[1]);
const expected = crypto.createHmac("sha256", secret)
  .update(ts + "." + rawBody).digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)))
  return res.status(400).end();
if (Math.abs(Date.now()/1000 - Number(ts)) > 300) return res.status(400).end();

Retries use exponential backoff; after 8 attempts the delivery is dead-lettered and visible in the dashboard. Event ids are stable across retries — deduplicate on id. Delivery order is not guaranteed.

Suppressions & unsubscribe

GET/POST /v1/suppressions manages tenant-level entries. Hard bounces and complaints are added automatically. Marketing messages includeList-Unsubscribe and one-clickList-Unsubscribe-Post headers; one-click endpoints posts to /v1/marketing/unsubscribe?token=….

Suppression is enforced at intake and re-checked immediately before submission. No autopilot action can override it.

Limits & retries

  • • 50 recipients per message; 1 MiB HTML body.
  • • 50,000 messages/month on the pilot plan, hard limit.
  • • 4xx/5xx classified by enhanced status; hard bounces are never retried.
  • • Exponential backoff with jitter, capped at 45 minutes and 6 attempts.
  • • Per-tenant token buckets and per-provider budgets prevent monopolisation.

Errors

Errors use a stable envelope. code is machine-readable;message is safe to surface to operators.

error envelope
{
  "error": {
    "code": "domain_not_verified",
    "message": "sender domain is not verified; complete DNS setup first",
    "details": { "status": "PENDING" }
  }
}

401 invalid_api_key

Key missing, revoked or malformed

403 domain_not_owned

From domain is not in this workspace

403 domain_not_verified

DNS verification incomplete

409 idempotency_conflict

Key reused with a different body

429 quota_exceeded

Monthly plan limit reached

Acceptable use

Permission-based sending only. Purchased, rented or scraped lists are prohibited, as is any attempt to evade mailbox-provider enforcement, forge authentication or route rejected traffic through alternative pools. Marketing streams require documented consent provenance and functioning unsubscribe.

We verify domain ownership, review use cases during onboarding, enforce graduated quotas, and suspend on complaint or abuse escalation. Appeals are reviewed by a human with the underlying evidence preserved.

Need a hand?

Deliverability and integration questions are answered by engineers, not a bot.

[email protected]