API reference
Manage domains, addresses and SMTP passwords for Sendas. Create an API key in the dashboard and send it as `Authorization: Bearer sk_...`.
Authentication
Create a key under API keys and send it with every request. Keys can do everything the dashboard can, except manage other keys.
curl https://sendas.space/api/v1/me \ -H "Authorization: Bearer sk_..."
Responses are JSON: { "data": … } on success and { "error": { "code", "message" } } on failure.
Webhooks
Add an HTTPS endpoint under Webhooks or with POST /api/v1/webhooks, and Sendas posts an event within seconds of each message: message.received, message.sent, message.bounced and message.rejected. Events carry the same metadata as the activity log, never the message body.
{
"id": "7d0c5c0e-5a1b-4f7e-9a51-0b6f1f3c2d11",
"type": "message.received",
"createdAt": "2026-11-03T08:15:02.114Z",
"data": {
"id": "18342",
"domain": "yourcompany.com",
"address": "hr@yourcompany.com",
"direction": "inbound",
"status": "forwarded",
"from": "candidate@gmail.com",
"to": "hr@yourcompany.com",
"subject": "Application: Backend Engineer",
"messageId": "<CAF3x@mail.gmail.com>",
"reason": null,
"createdAt": "2026-11-03T08:15:02.114Z"
}
}Verifying webhooks
Every request has a Sendas-Signature: t=…,v1=… header: an HMAC-SHA256 of "<t>.<raw body>" with your webhook’s secret. Check it before trusting the event.
import crypto from 'node:crypto'
// rawBody: the request body exactly as received, before JSON parsing
function verifySendas(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')))
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false // older than 5 minutes
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
return v1?.length === expected.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
}Retries
Answer with any 2xx status within 10 seconds. Otherwise Sendas tries again after 1 minute, 5 minutes, 30 minutes, 2 hours and 8 hours, then gives up on that event. A retry keeps the same id, so skip ids you have already handled. After 50 failures in a row the webhook is switched off until you switch it back on. Redirects are not followed, and URLs that point to private or local addresses are refused.
Endpoints
- get
/api/v1/meCurrent account and plan limits
- get
/api/v1/domainsList domains
- post
/api/v1/domainsAdd a domain
{"name":"yourcompany.com"} - get
/api/v1/domains/{id}Get a domain with the DNS records to publish
- delete
/api/v1/domains/{id}Remove a domain and all its addresses
- post
/api/v1/domains/{id}/verifyCheck DNS records; the domain goes live once all required records are found
- get
/api/v1/domains/{id}/aliasesList addresses
- post
/api/v1/domains/{id}/aliasesCreate an address; mail is forwarded to your Gmail
{"localPart":"hr"} - get
/api/v1/domains/{id}/logsMessage log for the last 7 days (metadata only, never message bodies)
- patch
/api/v1/aliases/{id}Pause or resume an address
{"active":false} - delete
/api/v1/aliases/{id}Delete an address
- get
/api/v1/aliases/{id}/smtp-credentialsList live SMTP passwords (the passwords themselves are never shown again)
- post
/api/v1/aliases/{id}/smtp-credentialsCreate an SMTP password for Gmail "Send mail as". Returned once.
{"label":"Gmail"} - get
/api/v1/aliases/{id}/forwardsTeammates who also receive this address’s mail
- post
/api/v1/aliases/{id}/forwardsInvite a teammate. They get a confirmation email; nothing is forwarded until they confirm. Up to 5 per address.
{"email":"teammate@gmail.com"} - delete
/api/v1/forwards/{id}Stop forwarding to a teammate
- delete
/api/v1/smtp-credentials/{id}Revoke an SMTP password
- get
/api/v1/webhooksList webhooks with their last delivery result
- post
/api/v1/webhooksAdd an HTTPS endpoint for message events (up to 3). Leave out events to get all of them. The signing secret is returned once.
{"url":"https://example.com/hooks/sendas","events":["message.received","message.bounced"]} - patch
/api/v1/webhooks/{id}Change the URL or events, or switch a webhook off and on. Switching on resets its failure count.
{"url":"","events":"","active":false} - delete
/api/v1/webhooks/{id}Delete a webhook
- post
/api/v1/webhooks/{id}/testSend a webhook.test event to the endpoint within a few seconds