The Email service sends transactional email — sign-up confirmations, password resets, receipts, alerts — through a simple REST API. Authenticate with an API key, send a JSON payload, and track delivery in real time.
Concepts
Section titled “Concepts”| Email service | A logical sender, scoped to a workspace. You can have several services per workspace (for example one per app or per environment). |
| Sending domain | A domain you own that has been verified for sending email. |
| API key | The credential used to authenticate API requests. |
| Sandbox mode | A safe mode for testing that only delivers to verified recipients. |
Create an email service
Section titled “Create an email service”POST /api/email/servicesContent-Type: application/json
{ "name": "production-app"}Each service starts in sandbox mode. To send to any recipient, switch the service to production mode after verifying at least one sending domain.
Sending domains
Section titled “Sending domains”Add and verify the domain you want to send from.
POST /api/email/services/{service_id}/domainsContent-Type: application/json
{ "domain": "mail.example.com"}The response includes the DNS records you need to add at your registrar:
| Record | Purpose |
|---|---|
SPF (TXT) | Authorizes Runsite to send on behalf of your domain. |
DKIM (TXT, two keys) | Cryptographically signs outgoing messages so recipients can verify authenticity. |
DMARC (TXT, optional but recommended) | Tells receivers how to handle messages that fail SPF/DKIM. |
| MX (optional) | Required only if you want to receive bounces back through Runsite. |
After the records propagate, trigger verification:
POST /api/email/services/{service_id}/domains/{domain_id}/verifyOnce a domain is verified, you can send From: any address at that domain.
Sandbox mode
Section titled “Sandbox mode”While the service is in sandbox mode, only verified recipient addresses receive email. Other addresses are silently dropped (and logged as dropped: sandbox). Use this for early development.
Verify a sandbox recipient:
POST /api/email/services/{service_id}/sandbox-emailsContent-Type: application/json
{ "email": "you@example.com"}The recipient receives a verification link they must click before they can be used.
API keys
Section titled “API keys”POST /api/email/services/{service_id}/api-keysContent-Type: application/json
{ "name": "production-server"}The secret is returned once. Store it in your application’s environment, not in source.
Sending an email
Section titled “Sending an email”POST /api/email/services/{service_id}/sendAuthorization: Bearer rsk_email_...Content-Type: application/json
{ "from": "Acme <hello@mail.example.com>", "to": ["user@gmail.com"], "subject": "Welcome to Acme", "html": "<p>Thanks for signing up!</p>", "text": "Thanks for signing up!"}Optional fields:
| Field | Description |
|---|---|
cc, bcc | Standard recipient lists. |
reply_to | Override the Reply-To: header. |
headers | Custom headers as a JSON object. |
attachments | Array of { filename, content_base64, content_type }. |
tags | Array of strings for filtering stats and webhooks. |
Node.js example
Section titled “Node.js example”const res = await fetch( `https://api.runsite.app/api/email/services/${serviceId}/send`, { method: 'POST', headers: { Authorization: `Bearer ${process.env.RUNSITE_EMAIL_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ from: 'Acme <hello@mail.example.com>', to: ['user@gmail.com'], subject: 'Welcome to Acme', html: '<p>Thanks for signing up!</p>', }), },);Python example
Section titled “Python example”import os, requests
requests.post( f"https://api.runsite.app/api/email/services/{service_id}/send", headers={"Authorization": f"Bearer {os.environ['RUNSITE_EMAIL_KEY']}"}, json={ "from": "Acme <hello@mail.example.com>", "to": ["user@gmail.com"], "subject": "Welcome to Acme", "html": "<p>Thanks for signing up!</p>", },)Webhooks
Section titled “Webhooks”Subscribe to delivery events to react in your application — for example, mark a recipient as bounced or count complaints.
POST /api/email/services/{service_id}/webhooksContent-Type: application/json
{ "url": "https://example.com/webhooks/email", "events": ["delivered", "bounced", "complained", "opened", "clicked"]}Every event is signed; verify the signature header before trusting the payload.
| Event | When it fires |
|---|---|
sent | Runsite has accepted the message and queued it for delivery. |
delivered | The receiving server accepted the message. |
bounced | The receiving server rejected the message (hard or soft bounce). |
complained | The recipient marked the message as spam. |
opened | The recipient opened the email (when tracking pixels are enabled). |
clicked | The recipient clicked a tracked link. |
GET /api/email/services/{service_id}/stats?period=7dReturns counts for sent, delivered, bounced, complained, and dropped messages, optionally bucketed by day or hour.
Rate limits
Section titled “Rate limits”Each service has a per-minute and per-day send rate. Limits are visible on the service detail page and increase automatically with sending reputation.
Suppression list
Recipients that hard-bounce or mark messages as spam are added to a per-service suppression list automatically. You can view and manage the list from the dashboard or API.