Email API reference

Base URL: https://api.signally.cloud. All requests and responses are JSON. Create API keys under API & SMTP in your dashboard.

Authentication

Authorization: Bearer avt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys can send from any verified domain of your account. Keep them secret; revoke compromised keys immediately.

POST /v1/email

Send one message to up to 50 recipients (to + cc + bcc). Costs one credit per recipient.

{
  "from": { "address": "[email protected]", "name": "Your Shop" },  // or "[email protected]"
  "to": [{ "address": "[email protected]", "name": "Jane" }],          // or ["[email protected]"]
  "cc": [], "bcc": [],
  "reply_to": ["[email protected]"],
  "subject": "Your order has shipped",
  "html": "<p>Hi Jane…</p>",
  "text": "Hi Jane…",                                   // html and/or text
  "attachments": [{ "filename": "invoice.pdf", "content": "<base64>", "content_type": "application/pdf" }],
  "campaign": "order-updates",                          // groups the message in analytics
  "tags": { "order_id": "1042" },                       // up to 8, letters/numbers/_/-
  "headers": { "X-Order": "1042" }
}

Response 202:

{ "message_id": "0100018f…", "accepted": 1, "suppressed": [] }

Recipients on your suppression list (hard bounces, spam reports, unsubscribes) are skipped, not charged, and listed in suppressed.

POST /v1/email/batch

One template, up to 500 recipients, each sent individually and personalised with {{merge}} tags (HTML-escaped automatically).

{
  "from": "[email protected]",
  "subject": "Hi {{name}}, your code is {{code}}",
  "html": "<p>Your code is <b>{{code}}</b></p>",
  "campaign": "otp",
  "recipients": [
    { "address": "[email protected]", "name": "Jane", "merge_data": { "code": "4821" } },
    { "address": "[email protected]", "name": "Otieno", "merge_data": { "code": "9930" } }
  ]
}

Response 207 with a result per recipient: { "results": [{ "address", "message_id" } | { "address", "error": { "code", "message" } }], "sent": 2 }

GET /v1/messages/{message_id}

Message details and its events (Send, Delivery, Open, Click, Bounce, Complaint, …).

GET /v1/account

Your credit balance, daily limit and account status.

Errors

{ "error": { "code": "insufficient_credits", "message": "Not enough credits…" } }
StatusCodeMeaning
400invalid_from, invalid_recipient, missing_subject, missing_body, too_many_recipients, invalid_tag, invalid_headerFix the request
401unauthorizedMissing or revoked API key
402insufficient_creditsTop up under Billing
403sender_not_verified, account_not_activeVerify the domain / wait for approval
413attachments_too_largeAttachments over 7 MB in total
429rate_limited, daily_limit_reachedRetry with backoff / limit resets 00:00 UTC
5xxupstream_errorRetry with backoff

SMTP

Host smtp.signally.cloud, port 465 (SSL/TLS), username and password from an SMTP login in your dashboard. The From address must be on a verified domain.

Webhooks

Events: sent, delivered, bounced, complained, opened, clicked, rejected, delayed, failed, unsubscribed. Each POST carries X-Webhook-Signature: t=<unix>,v1=<hex> where v1 = HMAC-SHA256(secret, t + "." + rawBody).

{ "event": "bounced", "message_id": "0100018f…", "recipient": "[email protected]", "timestamp": "2026-10-18T09:12:44.000Z",
  "campaign": "order-updates", "campaign_id": null, "bounce": { "type": "Permanent", "subtype": "General", "diagnostic": "smtp; 550 5.1.1 user unknown" },
  "webhook_id": 3 }