Velaby Novacraft

Vela API reference

One key sends mail. Inbound needs no key at all. It arrives over SMTP and Vela writes it into whichever destination you configured. Create a key under API keys in the dashboard; the secret is shown once.

Keys

Send the key as a bearer token. A key carries a permission: full sends and reads, sending only sends, and read only reads. It also carries an expiry you choose when you create it, or no expiry at all. Revoking a key refuses every request made with it from that moment.

curl https://vela.novacraft.africa/api/v1/emails \
  -H "Authorization: Bearer vela_live_…"

Sending

POST/api/v1/emailssending

Send one message as a verified domain. Resend-shaped, so a client written against that API needs only a new base URL.

curl -X POST https://vela.novacraft.africa/api/v1/emails \
  -H "Authorization: Bearer vela_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Your invoice is ready",
    "html": "<p>Thanks, it is attached below.</p>",
    "attachments": [
      { "filename": "invoice.pdf", "content": "JVBERi0xLjcK…" }
    ]
  }'

# → { "id": "b7c3…" }

Each attachment carries its bytes as base64 content; Vela does not fetch a path. Give one a content_id and it travels inline, drawn wherever the html says <img src="cid:…">. Up to 28 MB in total, and a scheduled send cannot carry files yet.

Errors

StatusNameMeans
401missing_api_keyNo bearer token on the request.
401invalid_api_keyThe key is unknown, revoked, or past its expiry.
403restricted_api_keyThe key may only read mail, and the call sends or changes something.
422validation_errorThe body is missing a field, or an address is malformed.
422all_recipients_suppressedEvery recipient is suppressed, so nothing was sent. When only some are, the rest are sent and the reply lists the skipped ones under suppressed.
502delivery_failedThe provider refused the message. The attempt is still recorded against the key.

Suppressions

An address that bounces for good or reports spam is suppressed, and Vela will not send to it again. Bounces and complaints also land on a list every workspace shares, because one sender reputation carries all of them. Check recipients before you send to show the reason in your own interface instead of finding a skipped address afterwards.

POST/api/v1/suppressions/checkany key

Which of up to 200 addresses this workspace will not send to, why, and whether the shared list holds them. Only the addresses asked about are answered.

GET/api/v1/suppressionsany key

The workspace list, newest first. Pass ?limit= for up to 1000.

POST/api/v1/suppressionssending or full

Add an address with reason bounce, complaint or manual. It joins this workspace only; the shared list fills from bounces and complaints Vela itself receives.

DELETE/api/v1/suppressions?address=sending or full

Lift the workspace entry. The reply carries still_blocked when the shared list holds the address too; only Novacraft can lift that one.

curl -X POST https://vela.novacraft.africa/api/v1/suppressions/check \
  -H "Authorization: Bearer vela_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "addresses": ["[email protected]", "[email protected]"] }'

# → { "object": "list", "data": [
#     { "address": "[email protected]", "reason": "bounce",
#       "detail": "smtp; 550 5.1.1 user unknown",
#       "createdAt": "2026-10-08T08:33:14.545Z", "shared": true } ] }

Inbound

Point the domain's MX at Vela and every message is accepted, checked against SPF, DKIM and DMARC, parsed, and written to each destination that matches. Four kinds: webhook, postgres, libsql and s3. A delivery that fails is retried with backoff and every attempt keeps the status it got back.

Webhook payload

POST https://api.yourapp.com/hooks/mail
svix-id:         msg_…
svix-timestamp:  1757980800
svix-signature:  v1,<base64>

{
  "type": "mail.received",
  "data": {
    "id": "…",
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Invoice #4821",
    "spf": "PASS",
    "dkim": "PASS",
    "dmarc": "PASS"
  }
}

Verifying the signature

Vela signs with the Svix scheme, so an existing Resend or Svix verifier works unchanged. Concatenate the id, the timestamp and the raw body with dots, HMAC-SHA256 it with the destination secret, and compare in constant time.

const signed = `${svixId}.${svixTimestamp}.${rawBody}`
const expected = createHmac('sha256', secret).update(signed).digest('base64')
// header is "v1,<expected>"

Turn on includeRaw for a destination and the original MIME travels with the payload, attachments included.

MCP

The same keys open an MCP server. Give an agent a read key to read the mailbox. Over MCP a key never both reads and sends: a full key is read-only here, and send_email takes a sending key. Mail an agent reads is written by outside senders, and an agent that could also send would let that mail tell it what to send.

{
  "mcpServers": {
    "vela": {
      "url": "https://vela.novacraft.africa/api/mcp",
      "headers": { "Authorization": "Bearer vela_live_…" }
    }
  }
}
ToolNeedsDoes
list_domainsread or fullDomains on the workspace, their direction, and whether DNS is verified.
list_messagesread or fullMessages newest first, filtered by direction, domain or a search string.
get_messageread or fullOne message with its headers, auth results and every delivery attempt.
list_destinationsread or fullDestinations and their last result. Credentials are never returned.
list_deliveriesread or fullDelivery attempts, filtered by pending, delivered or dead.
retry_deliveryfullQueue a failed delivery for another attempt.
send_emailsendingSend one message as a verified domain.
workspace_statusread or fullCounts of domains, destinations, messages and queued deliveries.

Dashboard routes

These authenticate with your signed-in session rather than an API key, and back the dashboard itself. They are documented so you can script against the same contract from a browser context.

RouteMethodsDoes
/api/v1/vela/domainsGET · POST · DELETESending and receiving domains, with the DNS records each one needs.
/api/v1/vela/destinationsGET · POST · DELETEWhere inbound mail is written: webhook, postgres, libsql or s3.
/api/v1/vela/messagesGETInbound and sent messages, with their attachments and raw MIME.
/api/v1/vela/eventsGETDelivery attempts, with the status each destination returned.
/api/v1/vela/keysGET · POST · DELETEAPI keys. Creating or revoking one asks for your password again.
/api/v1/vela/sendPOSTSend as a signed-in operator, without minting a key first.