# Sidemail Documentation for AI Agents Use these docs to integrate Sidemail email sending into applications. --- # Sidemail Agent Setup Source: https://sidemail.io/agent-setup/index.md # Sidemail Agent Setup Use this file when adding Sidemail email sending to an application with an AI coding agent. ## Goal Integrate Sidemail as the server-side email provider for transactional email. Sidemail can send emails through: - The Sidemail Email Sending API or SDKs, which should be used in application code. - The Sidemail MCP server, which should be used by AI tools to manage Sidemail resources such as domains, contacts, Messenger drafts, email queries, and test sends. Do not add the MCP server to production application code. MCP is for the agent or developer tool environment. ## Recommended Agent Prompt ```text Fetch https://sidemail.io/agent-setup/index.md and follow it to integrate Sidemail into this project. Detect the framework, add server-side email sending with SIDEMAIL_API_KEY, update tests, and do not commit secrets or send real emails without confirmation. Use Sidemail MCP only for Sidemail account operations such as domains, contacts, Messenger drafts, querying emails, or test sends. ``` ## Credentials - Store the API key in `SIDEMAIL_API_KEY`. - Keep the API key server-side only. - Never expose the key in frontend bundles, mobile apps, public repositories, logs, or screenshots. - Do not hardcode the API key in source files. - Do not send real emails during tests unless the user explicitly asks for a live test. ## Primary Docs - [API authentication](https://sidemail.io/docs/api/authentication/index.md) - [Email API reference](https://sidemail.io/docs/api/email/index.md) - [Email sending quickstart](https://sidemail.io/docs/email-sending-quickstart/index.md) - [MCP server setup](https://sidemail.io/docs/mcp-server/index.md) - [Node.js quickstart](https://sidemail.io/docs/send-emails-with-nodejs/index.md) - [Next.js quickstart](https://sidemail.io/docs/send-emails-with-nextjs/index.md) - [Python quickstart](https://sidemail.io/docs/send-emails-with-python/index.md) ## Implementation Checklist 1. Detect the application framework and runtime. 2. Add `SIDEMAIL_API_KEY` to the server environment configuration. 3. Install the official SDK when it fits the stack, or call the HTTPS API directly. 4. Create a small server-only email client or helper module. 5. Wire the requested transactional email events, for example welcome, password reset, login, trial, invoice, or notification email. 6. Use `templateName`, `templateId`, `markdown`, `html`, or `text` according to the app's needs. 7. Use a verified `fromAddress` domain before production. 8. Add tests that mock Sidemail calls rather than sending real email. 9. Document the required environment variable for deployment. ## API Rules - Send API requests over HTTPS only. - Authenticate with `Authorization: Bearer ${SIDEMAIL_API_KEY}`. - Include `Content-Type: application/json`. - Use `POST https://api.sidemail.io/v1/emails` for sending email. - Use `fromAddress` from a verified sending domain in production. - Keep total recipients across `toAddress`, `cc`, and `bcc` at 50 or fewer. - Use attachments only when needed and keep the combined Base64 size under 5 MB. ## MCP Setup Use MCP when the agent needs to inspect or manage Sidemail account resources. Prefer normal API or SDK integration for application code. ### Codex ```bash codex mcp add sidemail --env SIDEMAIL_API_KEY=replace-with-your-api-key -- npx -y @sidemail/mcp ``` ### Claude Desktop ```json { "mcpServers": { "sidemail": { "command": "npx", "args": ["-y", "@sidemail/mcp"], "env": { "SIDEMAIL_API_KEY": "replace-with-your-api-key" } } } } ``` ### Cursor ```json { "mcpServers": { "sidemail": { "command": "npx", "args": ["-y", "@sidemail/mcp"], "env": { "SIDEMAIL_API_KEY": "replace-with-your-api-key" } } } } ``` ## Acceptance Criteria - The app sends emails only from server-side code. - The Sidemail API key is read from environment variables. - Tests do not send real email by default. - Error handling does not leak secrets. - The implementation includes enough documentation for the next developer to configure `SIDEMAIL_API_KEY`. --- # API authentication Source: https://sidemail.io/docs/api/authentication/index.md # API authentication The Sidemail API uses API keys to authenticate requests. You can view and manage your API keys in the [Sidemail Dashboard](https://client.sidemail.io). All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail. Your API keys give full access to your account, so keep them safe. Do not share your secret API keys in public places like GitHub or client-side code. ## Using a library If you use a library, configure it with your API key. ```javascript const configureSidemail = require("sidemail"); const sm = configureSidemail({ apiKey: "replace-with-your-api-key" }); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); ``` ```ruby require "sidemail" sm = Sidemail.new(api_key: "replace-with-your-api-key") ``` ## Making API requests manually To authenticate, use the `Authorization` header in your HTTP request. Include these HTTP headers when making an API request: - `Content-Type: application/json` to tell Sidemail you're sending JSON data - `Authorization: Bearer replace-with-your-api-key` to authenticate the API request with your API key --- # Contact API Source: https://sidemail.io/docs/api/contacts/index.md # Contact profiles API methods ### Available API endpoints: - `POST https://api.sidemail.io/v1/contacts` — Create or update a contact - `POST https://api.sidemail.io/v1/contacts/query` — Query contacts - `GET https://api.sidemail.io/v1/contacts` — List all contacts - `GET https://api.sidemail.io/v1/contacts/{emailAddress}` — Find a contact - `DELETE https://api.sidemail.io/v1/contacts/{emailAddress}` — Delete a contact --- ## Contact Object A contact object represents a single contact profile. - `emailAddress` (string): Email address of the contact. - `identifier` (string, optional): Your unique identifier for the contact. - `id` (string): Sidemail's unique identifier of the contact. - `projectId` (string): Identifier of the project associated with the contact. - `createdAt` (string): ISO8601 date string when the contact was created. - `timezone` (string, optional): Timezone of the contact (e.g., "Europe/Prague"). - `timezoneUtcOffset` (number, optional): UTC offset in minutes based on the contact's timezone. - `isSubscribed` (boolean): Indicates whether the contact is subscribed. - `gravatarUrl` (string, optional): URL of the contact's Gravatar image, automatically generated from their email address. - `subscribedIp` (string, optional): IP address from which the contact subscribed. - `subscribedAt` (string, optional): ISO8601 date string when the contact subscribed. - `unsubscribedAt` (string, optional): ISO8601 date string when the contact unsubscribed. - `sourceType` (string): Source of the contact ("api", "bulk-import", "stripe", "hostedform"). - `updatedAt` (string): ISO8601 date string when the contact was last updated. - `groups` (array, optional): List of group IDs the contact belongs to. - `customProps` (array, optional): List of custom properties associated with the contact. - `keyName` (string): Name of the custom property. - `value` (string | number): Value of the custom property. - `updatedAt` (string): ISO8601 date string when the property was last updated. - `note` (string, optional): Additional notes about the contact. ### Example data: ```json { "id": "307f1f70bcf86cd799439011", "projectId": "507f1f77bcf86cd799439011", "emailAddress": "marry@lightling.com", "identifier": "123", "createdAt": "2019-08-15T13:20:39.160Z", "timezone": "Europe/Prague", "isSubscribed": true, "subscribedAt": "2019-08-15T13:20:39.160Z", "subscribedIp": "192.168.1.1", "unsubscribedAt": null, "sourceType": "api", "updatedAt": "2019-08-15T13:20:39.160Z", "groups": ["257f1f77bcf86cd799439011"], "customProps": [ { "keyName": "name", "value": "Marry Lightning", "updatedAt": "2019-08-15T13:20:39.160Z" }, { "keyName": "subscriptionPlan", "value": "premium", "updatedAt": "2019-08-15T13:20:39.160Z" } ], "note": "VIP customer" } ``` --- ## Create or update a contact `POST https://api.sidemail.io/v1/contacts` ```javascript const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "xxxxx" }); const response = await sidemail.contacts.createOrUpdate({ emailAddress: "john.doe@example.com", identifier: "123", customProps: { fullName: "John doe", pricingPlan: "premium", registeredAt: "2019-08-15T13:20:39.160Z", lastSeenAt: "2019-08-20T17:40:39.160Z", }, }); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") sm.contacts.create_or_update( emailAddress="john.doe@example.com", identifier="123", customProps={ "fullName": "John doe", "pricingPlan": "premium", "registeredAt": "2019-08-15T13:20:39.160Z", "lastSeenAt": "2019-08-20T17:40:39.160Z", # ... more of your contact data ... }, ) ``` ```ruby require "sidemail" sm = Sidemail.new(api_key: "replace-with-your-api-key") sm.contacts.create_or_update( emailAddress: "john.doe@example.com", identifier: "123", customProps: { fullName: "John doe", pricingPlan: "premium", registeredAt: "2019-08-15T13:20:39.160Z", lastSeenAt: "2019-08-20T17:40:39.160Z", # ... more of your contact data ... } ) ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $sm->contacts->createOrUpdate([ 'emailAddress' => 'john.doe@example.com', 'identifier' => '123', 'customProps' => [ 'fullName' => 'John doe', 'pricingPlan' => 'premium', 'registeredAt' => '2019-08-15T13:20:39.160Z', 'lastSeenAt' => '2019-08-20T17:40:39.160Z', // ... more of your contact data ... ], ]); ``` ```bash curl -X POST https://api.sidemail.io/v1/contacts \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "emailAddress": "john.doe@example.com", "identifier": "123", "customProps": { "fullName": "John doe", "pricingPlan": "premium", "registeredAt": "2019-08-15T13:20:39.160Z", "lastSeenAt": "2019-08-20T17:40:39.160Z" } }' ``` ### Parameters **identifier** `string`\ Unique string, that represent user in your system. It's recommended to use identifier and not just rely on email. --- **emailAddress** `string`\ User's email address. - If user changes email in your system, Sidemail will update the email value only if identifier was set. Otherwise, new contact will be created. --- **isSubscribed** `boolean` `optional`\ Specifies if Sidemail should set contact's status to subscribed or unsubscribed. --- **timezone** `string` `optional`\ Optionally set and update contact's timezone, this has to be [a valid timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). This is useful for timezone based delivery with Messenger. --- **customProps** `object`\ Specify values of properties here. Use exactly the same property key name (whitespace sensitive) as you used when creating the property. ​Property value has to have matching data type as you specified when creating the property. Otherwise, Sidemail returns validation error. Null is allowed. --- **groups** `array` `optional`\ An array where each item must be group ID `(string)`. To see a group ID, navigate to the contact page in your project, hover over a group and click the three-dot icon. The group ID is displayed below "Edit group" title in the edit modal window. ​Group ID has this format: `"5d45dd1ca546d200fe201f83"`. ### Returns If contact was newly created: ```javascript { "status": "created" } ``` or if contact was updated: ```javascript { "status": "updated" } ``` --- ## Find a contact `GET https://api.sidemail.io/v1/contacts/:emailAddress` Examples: ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.contacts.find({ emailAddress: "marry@lightning.com", }); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.contacts.find(emailAddress="marry@lightning.com") ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->contacts->find([ 'emailAddress' => 'marry@lightning.com' ]); ``` ```bash curl -X GET "https://api.sidemail.io/v1/contacts/marry@lightning.com" \ -H "Authorization: Bearer replace-with-your-api-key" ``` Retrieves the contact data. You need only supply the contact email address to the URL. ### Parameters *No parameters.* ### Returns Returns a contact object if contact was found or `null`. ```javascript { contact: { "id": "307f1f70bcf86cd799439011", "projectId": "507f1f77bcf86cd799439011", "emailAddress": "marry@lightling.com", "identifier": "123", "createdAt": "2019-08-15T13:20:39.160Z", "timezone": "Europe/Prague", "isSubscribed": true, "subscribedAt": "2019-08-15T13:20:39.160Z", "unsubscribedAt": null, "sourceType": "api", "updatedAt": "2019-08-15T13:20:39.160Z", "groups": ["257f1f77bcf86cd799439011"], "customProps": [ { "keyName": "name", "value": "Marry Lightning", "updatedAt": "2019-08-15T13:20:39.160Z" }, { "keyName": "subscriptionPlan", "value": "premium", "updatedAt": "2019-08-15T13:20:39.160Z" } ], "note": null } } ``` --- ## Query contacts (unstable) `POST https://api.sidemail.io/v1/contacts/query` Retrieve a list of your contacts, with support for advanced filtering, searching, and pagination. The contacts are returned sorted by creation date, with the most recent contacts appearing first. JSON payload example: ```json { "search": "@blueberry.com", "isSubscribed": true, "groupId": ["5d45dd1ca546d200fe201f83"], "contactId": ["5d45dd1ca546d200fe201f84"], "match": "all", "filter": { "match": "all", "rules": [ { "field": "customProps.plan", "operator": "includes", "value": "premium" }, { "field": "createdAt", "operator": "gte", "value": "2023-01-01T00:00:00.000Z" } ] }, "offset": 0, "limit": 20 } ``` ### Parameters **contactId** `string` or `string[]`\ Filter by contact ID(s). Supports a single ID or array of IDs. --- **groupId** `string` or `string[]`\ Filter by group ID(s). Supports a single ID or array of IDs. --- **search** `string`\ Partial email address search, case-insensitive. --- **isSubscribed** `boolean` `optional`\ Filter by subscription status. --- **match** `"all"` or `"any"` `optional`\ Combine top-level filters with AND (all) or OR (any). Default is `"all"`. --- **filter** `object` `optional`\ Advanced filter object (see below). --- **offset** `number` `optional`\ Number of contacts to skip (pagination). Default: `0`. --- **limit** `number` `optional`\ Number of contacts per page. Default: `20`. ### Advanced filter object (`filter`) The `filter` object allows for complex, rule-based filtering: - `match`: "all" or "any" (default: "all") — whether all rules must match or any rule can match. - `rules`: Array of rule objects. Each rule: - `field`: string (required) — the field to filter on (any contact property, e.g., `customProps.keyName`, `emailAddress`, `createdAt`) - `operator`: string (default is `equals`) — one of: `equals`, `notEquals`, `includes`, `notIncludes`, `gt`, `gte`, `lt`, `lte`, `in`, `notIn`, `anyValue`, `noValue` - `value`: string or number — the value to compare (can be empty string or null for some operators) Example filter rule: ```json { "filter": { "match": "all", "rules": [ { "field": "timezone", "operator": "includes", "value": "Europe" }, { "field": "customProps.registeredAt", "operator": "gt", "value": "2024-01-01T00:00:00.000Z" }, { "field": "customProps.plan", "value": "pro" } ] } } ``` ### Returns Returns an object with the following structure: ```json { "data": [ /* Array of contact objects */ ], "pageCount": 5, // Total number of pages "totalCount": 42 // Total number of contacts matching the query } ``` ## List all contacts `GET https://api.sidemail.io/v1/contacts` ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.contacts.list(); // to paginate: // const response = await sidemail.contacts.list({ paginationCursorNext: "123" }); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.contacts.list() # paginate: resp = sm.contacts.list(paginationCursorNext="123") ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->contacts->list(); // paginate: $response = $sm->contacts->list([ 'paginationCursorNext' => '123' ]); ``` ```bash curl -X GET "https://api.sidemail.io/v1/contacts" \ -H "Authorization: Bearer replace-with-your-api-key" ``` Returns a list of your contacts. The contacts are returned sorted by creation date, with the most recent contacts appearing first. ### Parameters **paginationCursorNext** `string`\ A cursor for use in pagination. `paginationCursorNext` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with `obj_foo`, your subsequent call can include `paginationCursorNext=obj_foo` in order to fetch the next page of the list. ### Returns A object with a data property that contains an array of up to 100 contacts. Each entry in the array is a separate contact object. If no more contacts are available, the resulting array will be empty. ```javascript { hasMore: true, paginationCursorNext: "307f1f70bcf86cd799439011", data: [ { "id": "307f1f70bcf86cd799439011", "projectId": "507f1f77bcf86cd799439011", "emailAddress": "marry@lightling.com", "identifier": "123", "createdAt": "2019-08-15T13:20:39.160Z", "timezone": "Europe/Prague", "isSubscribed": true, "subscribedAt": "2019-08-15T13:20:39.160Z", "unsubscribedAt": null, "sourceType": "api", "updatedAt": "2019-08-15T13:20:39.160Z", "groups": ["257f1f77bcf86cd799439011"], "customProps": [ { "keyName": "name", "value": "Marry Lightning", "updatedAt": "2019-08-15T13:20:39.160Z" }, { "keyName": "subscriptionPlan", "value": "premium", "updatedAt": "2019-08-15T13:20:39.160Z" } ], "note": null } // Another 99 contacts... ] } ``` --- ## Delete a contact `DELETE https://api.sidemail.io/v1/contacts/:emailAddress` ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.contacts.delete({ emailAddress: "marry@lightning.com", }); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.contacts.delete(emailAddress="marry@lightning.com") ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->contacts->delete([ 'emailAddress' => 'marry@lightning.com' ]); ``` ```bash curl -X DELETE "https://api.sidemail.io/v1/contacts/marry@lightning.com" \ -H "Authorization: Bearer replace-with-your-api-key" ``` Permanently deletes a contact. It cannot be undone. ### Parameters *No parameters.* ### Returns Returns an object with `deleted` parameter that indicates the outcome of the operation. ```javascript { "deleted": true } ``` --- # Domains API Source: https://sidemail.io/docs/api/domains/index.md # Domains API methods (unstable) Sidemail enables you to manage and verify custom domains for sending emails on behalf of your brand. Before you can send from your own domain, it must be added and verified (including DNS authentication such as DKIM). This API lets you list, add, and remove domains, as well as view their verification and DNS setup status. ### Available API endpoints: - `GET https://api.sidemail.io/v1/domains` — List all sending domains - `POST https://api.sidemail.io/v1/domains` — Create a sending domain - `DELETE https://api.sidemail.io/v1/domains/{id}` — Delete a sending domain --- ## Domain Object A Domain object represents a verified sending domain used for email delivery. - `id` (string): Unique identifier of the domain (object ID). - `domain` (string, **required**): The domain name (e.g., "example.com"). - `status` (string): Verification status. One of `pending`, `success`, `failed`. - `dkim` (boolean): Whether DKIM is set up (true/false). - `dkimTokens` (array of strings): DKIM selector tokens used for DNS records. - `mailFromDomain` (string): The custom MAIL FROM domain used for email delivery. - `mailFromStatus` (string): Status of the MAIL FROM domain (e.g., `pending`, `success`, `failed`). - `dns` (array of objects): List of DNS records required for setup. Each object has: - `hostname` (string): The DNS record hostname. - `value` (string): The value to set for the DNS record. - `dcUrl` (string): Domain Connect setup URL for automated DNS setup. ### Example Domain Object ```json { "id": "5d45dd1ca546d200fe201f84", "domain": "example.com", "status": "pending", "dkim": true, "dkimTokens": ["xxxxx"], "mailFromDomain": "out.mail.example.com", "mailFromStatus": "pending", "dns": [ { "hostname": "xxxxx._domainkey.out.mail.example.com", "value": "xxxxx.dkim.sidemail.net" }, { "hostname": "out.mail.example.com", "value": "xxxxx.mailfrom.sidemail.net" } ], "dcUrl": "https://dash.cloudflare.com/domainconnect/v2/d..." } ``` --- ## List all domains **`GET`** `https://api.sidemail.io/v1/domains` Returns a list of all sending domains for the authenticated organization. **Response:** ```json { "data": [ { /* Domain object */ }, ... ] } ``` --- ## Create a sending domain **`POST`** `https://api.sidemail.io/v1/domains` Begins the sending domain set up process. Requires a domain name in the request body. - To verify a domain, you must add the required DNS records provided in the `dns` field of the domain object. - Use the `dcUrl` for automated DNS setup if your provider is Cloudflare. - The domain status transitions from `pending` → `success` (when verified) or `failed` (if verification fails). **Request Body:** ```json { "domain": "example.com" } ``` **Response:** ```json { /* Domain object */ } ``` --- ## Delete a sending domain **`DELETE`** `https://api.sidemail.io/v1/domains/:id` Deletes a sending domain. The domain must not be used in any active automations or scheduled emails. **Response:** ```json { "deleted": true } ``` --- --- # Sidemail API reference — email Source: https://sidemail.io/docs/api/email/index.md # Email API methods ### Available API endpoints: - `POST https://api.sidemail.io/v1/email/send` — Send an email - `POST https://api.sidemail.io/v1/email/search` — Query emails - `POST https://api.sidemail.io/v1/email/validate` — Validate an email address - `GET https://api.sidemail.io/v1/email/{id}` — Retrieve an email - `DELETE https://api.sidemail.io/v1/email/{id}` — Delete an email --- ## Email Object An email object represents a single email sent or scheduled. - `id` (string): Unique identifier of the email. - `projectId` (string): Identifier of the project associated with the email. - `automationId` (string, optional): Identifier of the automation associated with the email. - `messengerId` (string, optional): Identifier of the messenger associated with the email. - `toAddress` (string): Recipient's email address. - `toAddress` (string): Recipient's email address. - `cc` (array, optional): Carbon copy recipients. Each item is an object with `email` and optional `name`. - `bcc` (array, optional): Blind carbon copy recipients. Each item is an object with `email` and optional `name`. - `fromName` (string, optional): Sender's display name. - `fromAddress` (string): Sender's email address. - `replyToName` (string, optional): Display name for the reply-to address. - `replyToAddress` (string, optional): Reply-to email address. - `subject` (string): Subject of the email. - `templateId` (string, optional): Identifier of the template used. - `templateName` (string, optional): Name of the template used. - `templateProps` (object, optional): Key-value pairs for template variables. - `status` (string): Status of the email ("queued", "delivered", "open", "bounce", "error", "complaint", "suppressed", "scheduled") - `isOpenTracked` (boolean): Whether open tracking is enabled. - `createdAt` (string): ISO8601 date string when the email was created. - `scheduledAt` (string, optional): ISO8601 date string for scheduled delivery. - `text` (boolean, optional): Indicates if the email has plain text content. - `htmlReferenceUrl` (string, optional): Opaque short-lived URL to preview the HTML content of the email. - `events` (array, optional): List of events associated with the email (e.g., open, click). - `type` (string): Type of the event. - `time` (string): ISO8601 date string of the event. - `ipAddress` (string, optional): IP address associated with the event. - `userAgent` (string, optional): User agent associated with the event. - `attachments` (array, optional): List of attachments. - `id` (string): Identifier of the attachment. - `name` (string): Name of the attachment. - `size` (number): Size of the attachment in bytes. - `contentType` (string): MIME type of the attachment. - `cid` (string, optional): Content-ID used for inline attachments. - `contentDisposition` (string, optional): Attachment disposition (`attachment` or `inline`). - `fileUrl` (string, optional): URL to download the attachment. --- ## Send email `POST https://api.sidemail.io/v1/email/send` Note: Inside the Sidemail dashboard, you'll find pre-generated, ready-to-copy code examples for multiple languages, including **PHP**, **Ruby**, **Python**, **cURL**, and more. These examples are tailored to your specific email templates, so you can quickly copy, customize, and start sending emails with ease. ```javascript const configureSidemail = require("sidemail"); const sm = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sm.sendEmail({ toAddress: "user@email.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Welcome", }); ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'templateName' => 'Welcome', ]); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.send_email( toAddress="user@email.com", fromAddress="you@example.com", fromName="Your app", templateName="Welcome", ) ``` ```ruby require "sidemail" sm = Sidemail.new(api_key: "replace-with-your-api-key") response = sm.send_email( toAddress: "user@email.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Welcome" ) ``` ```go package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" ) type EmailRequest struct { ToAddress string `json:"toAddress"` FromAddress string `json:"fromAddress"` FromName string `json:"fromName"` TemplateName string `json:"templateName"` } func main() { apiKey := "replace-with-your-api-key" url := "https://api.sidemail.io/v1/emails" email := EmailRequest{ ToAddress: "user@email.com", FromAddress: "you@example.com", FromName: "Your app", TemplateName: "Welcome", } jsonData, err := json.Marshal(email) if err != nil { fmt.Println("Error encoding data:", err) return } req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { fmt.Println("Error creating request:", err) return } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+apiKey) client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println("Error sending request:", err) return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode == 200 { // Handle success fmt.Println("Email sent successfully") } else { // Handle error fmt.Printf("Error: %s\n", string(body)) } } ``` ```bash curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@email.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Welcome" }' ``` ## Parameters **toAddress** `string`\ A valid email address that will receive the email. - Only ASCII characters are supported. --- **cc** `string | object | array` `optional`\ Add carbon copy recipients to the email. - Accepts a single email string, a single object, or an array of strings/objects. - Object form requires `email` and supports optional `name`. - Only ASCII characters are supported. - The total number of recipients across `toAddress`, `cc`, and `bcc` must be 50 or fewer. Examples: ```javascript // Single string { "cc": "jane@example.com" } // Array of multiple CCs { "cc": ["alice@example.com", "bob@example.com"] } // Single object with name { "cc": { "email": "jane@example.com", "name": "Jane Doe" } } ``` --- **bcc** `string | object | array` `optional`\ Add blind carbon copy recipients to the email. - Accepts a single email string, a single object, or an array of strings/objects. - Object form requires `email` and supports optional `name`. - Only ASCII characters are supported. - The total number of recipients across `toAddress`, `cc`, and `bcc` must be 50 or fewer. - BCC recipients are not visible to other recipients in delivered headers. Examples: ```javascript // Single string { "bcc": "owner@example.com" } // Array of multiple BCCs { "bcc": ["records@example.com", "legal@example.com"] } // Single object with name { "bcc": { "email": "auditor@example.com", "name": "Audit" } } ``` --- **subject** `string`\ An email subject line. - All UTF-8 characters (emojis are supported). --- **fromName** `string` `optional`\ Display name (also known as friendly name) that appears before the `fromAddress` email address. For example: Sidemail or Patrik from Sidemail. - It has to have at least 1 character and less than 100 characters. - All UTF-8 characters (emojis are supported). --- **fromAddress** `string`\ The email address from which you want to send the email. For example: [info@sidemail.io](mailto:info@sidemail.io). - Only [verified sending domains](/docs/sending-identities) or pre-generated by Sidemail - Only ASCII characters are supported. ​ To define a display name (a name before the actual email address), use `fromName`. --- **replyToAddress** `string` `optional`\ If you want the recipient of an email to reply to a different email address than `fromAddress`, specify it with `replyToAddress`. - Only ASCII characters are supported. --- **replyToName** `string` `optional`\ Use in conjunction with `replyToAddress` to show a friendly name for the `replyToAddress`. Ignored if `replyToAddress` is not specified. - It has to have at least 1 character and less than 100 characters. - All UTF-8 characters (emojis are supported). --- **templateId** `string` `optional`\ A template ID of the template you want to send. Cannot be used together with `templateName`, `html` or `text` or `markdown`. --- **templateName** `string` `optional`\ A template name of the template you want to send. Cannot be used together with `templateId`, `html` or `text` or `markdown`. --- **templateProps** `object` `optional`\ Pass data to template props here. Use exactly the same key name (whitespace sensitive) as you did in your email template. If a variable has been defined in your email template, but you didn't add it to `templateProps` when making an API request, the key name of the variable will be used as a fallback.​ For example: if you defined `{firstName}` template prop inside of the email template you want to send, the `templateProps` parameter should include a key `firstName` with a value that is `string`. ```javascript { "firstName": "Patrik" } ``` Learn more about [dynamic data with template props](/docs/template-props). --- **markdown** `string` `optional`\ To send markdown content which Sidemail turns into branded transactional email, pass the markdown as a `string` into the `markdown` parameter. - Cannot be used together with `templateId` or `templateName` or `html` or `text`. - Must be less than 102 400 characters long (100 kB). [Learn more about sending markdown content →](/docs/markdown-emails/) --- **html** `string` `optional`\ To send your own HTML email, pass your HTML as a `string` into the `html` parameter. - Cannot be used together with `templateId` or `templateName` or `markdown`. - Must be less than 1 024 000 characters long (1 MB). Note that Gmail cuts off email preview if longer than 104 448 characters (102 kb). - Due to potential for abuse, new accounts created after January 30, 2023 must be manually approved to send custom HTML emails. Don't hesitate to contact us at [support@sidemail.io](mailto:support@sidemail.io) to get approved with legitimate use cases. Sidemail does not inline CSS, or process the custom HTML email in any other way. Do any processing in your application before sending if necessary. [Learn more about sending custom HTML emails →](/docs/custom-html-emails/) --- **text** `string` `optional`\ To send a plain-text email, pass `string` into the `text` parameter. - Combine with `html` to create both plain-text and HTML version of your email. - Cannot be used together with `templateId` or `templateName` or `markdown`. - Must be less than 102 400 characters long (100 kB). [Learn more about sending plain-text emails](/docs/plain-text-emails/). --- **isOpenTracked** `boolean` `optional`\ Whether the email should be open tracked or not. - Default is `true` — emails are open tracked by default. --- **scheduledAt** `date` `optional`\ Specify a delayed email delivery by providing a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date in the future. [Learn more about scheduled email delivery](/docs/scheduled-email-delivery/). --- **headers** `object` `optional`\ Specify any custom headers of an email. Both key and value must be a `string` type. Headers example: ``` "headers": { "X-Custom-Id": "12345" } ``` --- **attachments** `array` `optional` Attach a Base64 encoded file to an email. - The `attachments` array must contain a valid `attachment` object (described below). - The `attachments` array can contain multiple `attachment` objects. - The combined file size limit is 5 MB (which is equal to less than `5242880` characters long). Note that file size is calculated from the Base64 encoded string, which is on average 133% of the original file size. - Allowed files: `.jpg` `.jpeg` `.pdf` `.csv` `.html` `.png` `.gif` `.json` `.txt` `.docx` `.xlsx` `.pptx` **attachment** `object` `required` - **name** `string` `required` The name of the attached file that will show up to the recipient. - The file ending must be one of the allowed file types (as described above). - Must be less than 100 characters long. - Must contain at least 1 character before a valid file ending. - The content type is set automatically based on the file ending. - **content** `string` `required` The Base64 encoded file. - Must be valid Base64 ([RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648)) - Must be less than `5242880` characters long (5 MB). - **cid** `string` `optional` A Content-ID for referencing the attachment from custom HTML, for example ``. - Use the ID value without angle brackets. - Must be less than 255 characters long. - Must not contain whitespace or angle brackets. - When `cid` is provided, the attachment is sent inline by default. - **contentDisposition** `string` `optional` How the attachment should be presented. Must be either `attachment` or `inline`. Attachment example: ```javascript { "attachments": [ { "name": "file.txt", "content": "dmFsaWQgY29udGVudA==" } ] } ``` --- ### Returns Returns an object with an email ID and the current status on success. ```javascript { "id": "5e858953daf20f3aac50a3da", "status": "queued" } ``` When [sending a batch of emails in a single API call](/docs/batch-email-sending), it returns an ordered array of objects with email ID and the current status on success: ```javascript [ { id: "5e858953daf20f3aac50a3da", status: "queued", }, { id: "6e858953daf20f3aac50a3da", status: "queued", }, ]; ``` --- ## Search emails `POST https://api.sidemail.io/v1/email/search` Searches emails based on the provided query and returns found email data. This endpoint is paginated and it returns a maximum of 20 results per page. The email data are returned sorted by creation date, with the most recent emails appearing first. To list emails without any filter, exclude the `query` from the JSON payload. JSON payload example: ```javascript { "query": { "toAddress": "john.doe@example.com", "status": "delivered", "templateProps": { "color": "red" } } } ``` ### Parameters - **paginationCursorNext** `string` A cursor for use in pagination. `paginationCursorNext` is an object ID that defines your place in the list. For instance, if you make a list request and receive 20 objects, ending with `obj_foo`, your subsequent call can include `paginationCursorNext=obj_foo` in order to fetch the next page of the list. - **paginationCursorPrev** `string` A cursor for use in pagination to fetch the previous page of the list. - **limit** `number` The number of results to return per page. Must be between 1 and 100. Default is 20. - **query** `object` - **search** `string`: A text search query to match against email fields. - **toAddress** `string`: Recipient's email address. - **fromName** `string`: Sender's display name. - **fromAddress** `string`: Sender's email address. - **subject** `string`: Subject of the email. - **templateName** `string`: Name of the template used. - **templateId** `string` or `array`: Identifier(s) of the template(s) used. - **automationId** `string` or `array`: Identifier(s) of the automation(s) associated with the email. - **messengerId** `string` or `array`: Identifier(s) of the messenger(s) associated with the email. - **status** `string` or `array`: Status of the email (e.g., "queued", "delivered", "open"). - **scheduledAt** `date`: ISO8601 date string for scheduled delivery. - **templateProps** `object`: Key-value pairs to match against template variables. ### Returns A response object with a `data` property that contains an array of up to 20 emails. Each entry in the array is a separate email object. If no more emails are available, the resulting array will be empty. ```javascript { hasPrev: true, hasMore: true, paginationCursorPrev: "307f1f70bcf86cd799439011", paginationCursorNext: "407f1f70bcf86cd799439011", data: [ { "id": "307f1f70bcf86cd799439011", "projectId": "507f1f77bcf86cd799439011", "toAddress": "marry@lightling.com", "fromAddress": "hey@sidemail.io", "fromName": "Sidemail", "subject": "Some email subject", "templateId": "192f1f77bcf86cd799439011", "templateName": "Welcome", "templateProps": [{ "keyName": "name", "value": "Marry" }], "status": "delivered", "isOpenTracked": true, "createdAt": "2019-08-15T13:20:39.160Z", "attachments": null }, // Another 19 found emails... ] } ``` --- ## Retrieve email `GET https://api.sidemail.io/v1/email/{id}` Retrieves the email data. You need only supply the email ID to the URL. ### Parameters *No parameters.* ### Returns Returns an email object if email was found or `null`. ```javascript { email: { "id": "307f1f70bcf86cd799439011", "projectId": "507f1f77bcf86cd799439011", "toAddress": "marry@lightling.com", "fromAddress": "hey@sidemail.io", "fromName": "Sidemail", "subject": "Some email subject", "templateId": "192f1f77bcf86cd799439011", "templateName": "Welcome", "templateProps": [{ "keyName": "name", "value": "Marry" }], "status": "delivered", "isOpenTracked": true, "createdAt": "2019-08-15T13:20:39.160Z", "attachments": [ { "id": "907f1f77bcf86cd799439011", "name": "invoice.txt", "size": 51200 } ] } } ``` --- ## Delete email `DELETE https://api.sidemail.io/v1/email/{id}` Permanently deletes an email. It cannot be undone. Only scheduled emails which are yet to be send can be deleted. ### Parameters *No parameters.* ### Returns Returns an object with `deleted` parameter that indicates the outcome of the operation. ```javascript { "deleted": true } ``` --- ## Validate an email address `POST https://api.sidemail.io/v1/email/validate` Checks the address format, common domain typos, disposable providers, and MX records. Set `isDeep` to `true` to also ask the destination mail server whether it accepts the address. No email is sent. ```bash curl -X POST "https://api.sidemail.io/v1/email/validate" \ -H "Authorization: Bearer replace-with-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "email": "person@example.com", "isDeep": true }' ``` ### Parameters - **email** `string`, required: The address to validate. It is trimmed and converted to lowercase. - **isDeep** `boolean`, optional: Adds the SMTP mailbox check. Defaults to `false`. ### Returns The endpoint returns `200 OK` when validation ran successfully. An invalid address is still a successful API request; inspect `valid` in the first item in `results`. Validators run in order and stop after the first failure. ```json { "results": [ { "email": "person@example.com", "valid": true, "validators": { "regex": { "valid": true }, "typo": { "valid": true }, "disposable": { "valid": true }, "mx": { "valid": true }, "smtp": { "valid": true } }, "mx": [ { "exchange": "mx.example.com", "priority": 10 } ] } ], "validationCredits": { "remaining": 976 } } ``` Standard validation does not use a deep-validation credit. A deep request uses one credit only when an SMTP check returns a definitive result. Temporary mail server failures and cached results do not use a credit. When no credits remain, the endpoint returns `402` with `limit-exhausted`. A `409` response with `resource-busy` means another deep check for the same organization is reserving a credit; retry the request. --- # API errors Source: https://sidemail.io/docs/api/errors/index.md # API errors Sidemail uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the `2xx` range indicate success. Codes in the `4xx` range indicate that there was an issue with the request that was sent. Among other things, this could mean that you did not authenticate correctly, that you are requesting an action that you do not have authorization for, or that your request is malformed. Codes in the `5xx` range indicate a server error on the Sidemail's side. ## Error structure ```javascript { "developerMessage": "Not authorized.", "errorCode": "authentication-failed", "moreInfo": "https://sidemail.io/docs/authentication" } ``` --- **developerMessage** `string`\ A brief description of the error. Usually contains instruction on how to resolve the issue. --- **errorCode** `string`\ An error code. Use this to [gracefully handle errors](https://en.wikipedia.org/wiki/Graceful_exit). --- **moreInfo** `string`\ A link to an appropriate documentation page. --- ## Complete list of error codes - `authentication-required`\ You need to authenticate this API request with your API key that you can find in API section in Sidemail's dashboard. - `authentication-invalid`\ Check the authentication section on how to properly authenticate API calls. - `authentication-failed`\ Check the authentication section on how to properly authenticate API calls. - `account-subscription-expired`\ Your Sidemail subscription has expired or is past due. - `to-address-invalid`\ A valid email email address that will receive the email. Only ASCII characters are supported. - `from-address-unverified`\ Make sure the `fromAddress` is verified. To manage verified addresses and domains go to your project settings. - `parameters-invalid`\ Some JSON parameter failed validation. - `limit-exhausted`: A quota or credit limit for the requested operation has been exhausted. The error message identifies the relevant limit. - `resource-busy`: Another request is operating on the same limited resource. Retry the request. - `template-not-found`\ Provided template ID or template name doesn't exist. Both fields are case sensitive and whitespace sensitive. - `resource-duplicate` A resource with the provided unique value already exists. - `account-unverified`\ Your Sidemail account is not verified because you haven't verify your email address. - `email-sending-paused`\ Email sending was paused because your project has high bounce or complaint rate. - `account-suspended`\ Your Sidemail account was suspended. - `json-invalid`\ Invalid JSON body. - `request-too-large`\ The body of the request is too large for processing. - `resource-missing`\ The ID provided is not valid. Either the resource does not exist, or an ID for a different resource has been provided. - `access-denied`\ Returned when the requested feature is not allowed or a limit is exhausted. - `error-unknown`\ An unknown error occurred. --- # Inbound API Source: https://sidemail.io/docs/api/inbound/index.md # Inbound API methods Sidemail enables you to receive emails on your verified domains via inbound routes. An inbound route defines which email addresses on your domain should accept incoming mail and how they should respond. When a matching email arrives, Sidemail stores it and optionally fires an `email.received` [webhook](/docs/webhooks/). ### Available API endpoints: - `GET https://api.sidemail.io/v1/inbound/routes` — List all inbound routes - `POST https://api.sidemail.io/v1/inbound/routes` — Create an inbound route - `PATCH https://api.sidemail.io/v1/inbound/routes/{routeId}` — Update an inbound route - `DELETE https://api.sidemail.io/v1/inbound/routes/{routeId}` — Delete an inbound route - `GET https://api.sidemail.io/v1/inbound/emails` — List received emails - `GET https://api.sidemail.io/v1/inbound/emails/{receivedEmailId}` — Retrieve a received email --- ## Inbound Route Object An inbound route object represents a rule for accepting inbound email on a specific domain and address. - `id` (string): Unique identifier of the inbound route. - `domain` (string): The domain to receive email on (e.g., "example.com"). Must be a verified domain or its subdomain. - `localPart` (string): The local part before the `@` sign. Use `"*"` for a catch-all route that accepts all addresses on the domain. - `isCatchAll` (boolean): Whether the route is a catch-all (i.e. `localPart` is `"*"`). - `responseMode` (string): How the mail server responds during the SMTP transaction. One of `"accept"`, `"reject-temp"`, `"reject-perm"`. - `isEnabled` (boolean): Whether the route is active. - `createdAt` (string): ISO8601 date string when the route was created. - `updatedAt` (string): ISO8601 date string when the route was last updated. ### Example Inbound Route Object ```json { "id": "67e4f2b6d4b7a63a0f6f2e11", "domain": "example.com", "localPart": "*", "isCatchAll": true, "responseMode": "accept", "isEnabled": true, "createdAt": "2026-03-27T12:00:00.000Z", "updatedAt": "2026-03-27T12:00:00.000Z" } ``` --- ## Received Email Object A received email object represents a single inbound email received by Sidemail. - `id` (string): Unique identifier of the received email. - `destination` (string): The SMTP envelope recipient address that matched the inbound route. - `from` (object): Sender information with `email` (string) and `name` (string or null). - `to` (array): Header "To" recipients. Each item has `email` and `name`. - `cc` (array): Header "Cc" recipients. Each item has `email` and `name`. - `replyTo` (array): Header "Reply-To" addresses. Each item has `email` and `name`. - `subject` (string): Email subject line. - `text` (string): Plain text body of the email. - `htmlAvailable` (boolean): Whether the email contains an HTML part. - `attachments` (array): Attachment metadata. Each item has `name` (string), `contentType` (string), and `size` (number, bytes). - `headers` (object): Raw email headers. - `auth` (object): Authentication results (SPF, DKIM, etc.). - `envelope` (object): SMTP envelope data. - `inboundRouteId` (string): Identifier of the inbound route that matched. - `spam` (object): Spam analysis results with `score` (number), `threshold` (number), `isSpam` (boolean), `action` (string), and `symbols` (array of strings). - `receivedAt` (string): ISO8601 date string when the email was received. - `previewHtmlUrl` (string or null): Opaque short-lived URL to preview the HTML version. Present only when `htmlAvailable` is true. - `rawEmailUrl` (string or null): Opaque short-lived signed URL to download the raw email (RFC 822). ### Example Received Email Object ```json { "id": "67e4f2b6d4b7a63a0f6f2e11", "destination": "hi@example.com", "from": { "email": "john@acme.com", "name": "John" }, "to": [{ "email": "hi@example.com", "name": "Support" }], "cc": [{ "email": "ops@example.com", "name": null }], "replyTo": [{ "email": "billing@acme.com", "name": "Billing" }], "subject": "Invoice Q1", "text": "Hello, please find invoice attached...", "htmlAvailable": true, "attachments": [ { "name": "invoice-q1.pdf", "contentType": "application/pdf", "size": 84320 } ], "headers": {}, "auth": {}, "envelope": {}, "inboundRouteId": "67e4f2b6d4b7a63a0f6f2e10", "spam": { "score": 2.4, "threshold": 15, "isSpam": false, "action": "accept", "symbols": ["R_SPF_ALLOW", "R_DKIM_ALLOW", "MIME_GOOD"] }, "receivedAt": "2026-03-27T12:01:18.921Z", "previewHtmlUrl": "https://signed-url.example/...", "rawEmailUrl": "https://signed-url.example/..." } ``` --- ## List all inbound routes **`GET`** `https://api.sidemail.io/v1/inbound/routes` Returns a list of all inbound routes in the project. ```bash curl https://api.sidemail.io/v1/inbound/routes \ -H "Authorization: Bearer replace-with-your-api-key" ``` **Response:** ```json { "data": [ { "id": "67e4f2b6d4b7a63a0f6f2e11", "domain": "example.com", "localPart": "*", "isCatchAll": true, "responseMode": "accept", "isEnabled": true, "createdAt": "2026-03-27T12:00:00.000Z", "updatedAt": "2026-03-27T12:00:00.000Z" } ] } ``` --- ## Create an inbound route **`POST`** `https://api.sidemail.io/v1/inbound/routes` Creates a new inbound route. The domain must be a verified domain in your project, or a subdomain of one. ```bash curl -X POST https://api.sidemail.io/v1/inbound/routes \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "domain": "example.com", "localPart": "*", "responseMode": "accept", "isEnabled": true }' ``` **Parameters:** - `domain` (string, **required**): The domain to receive email on. Must be a verified domain or its subdomain. - `localPart` (string, optional, default: `"*"`): The local part of the email address (before `@`). Use `"*"` to create a catch-all route. Maximum 128 characters. Cannot contain `@`. - `responseMode` (string, optional, default: `"accept"`): How the mail server should respond. One of: - `"accept"` — Accept and store the email. - `"reject-temp"` — Temporarily reject (4xx SMTP response). The sending server may retry. - `"reject-perm"` — Permanently reject (5xx SMTP response). - `isEnabled` (boolean, optional, default: `true`): Whether the route should be active. **Response:** ```json { "created": { "id": "67e4f2b6d4b7a63a0f6f2e11", "domain": "example.com", "localPart": "*", "isCatchAll": true, "responseMode": "accept", "isEnabled": true, "createdAt": "2026-03-27T12:00:00.000Z", "updatedAt": "2026-03-27T12:00:00.000Z" } } ``` --- ## Update an inbound route **`PATCH`** `https://api.sidemail.io/v1/inbound/routes/:routeId` Updates an existing inbound route. You can change `responseMode` and `isEnabled`. At least one field must be provided. ```bash curl -X PATCH https://api.sidemail.io/v1/inbound/routes/67e4f2b6d4b7a63a0f6f2e11 \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "responseMode": "reject-perm", "isEnabled": false }' ``` **Parameters:** - `responseMode` (string, optional): One of `"accept"`, `"reject-temp"`, `"reject-perm"`. - `isEnabled` (boolean, optional): Whether the route is active. **Response:** ```json { "updated": { "id": "67e4f2b6d4b7a63a0f6f2e11", "domain": "example.com", "localPart": "*", "isCatchAll": true, "responseMode": "reject-perm", "isEnabled": false, "createdAt": "2026-03-27T12:00:00.000Z", "updatedAt": "2026-03-27T12:05:00.000Z" } } ``` --- ## Delete an inbound route **`DELETE`** `https://api.sidemail.io/v1/inbound/routes/:routeId` Deletes an inbound route. Existing received emails are not affected. ```bash curl -X DELETE https://api.sidemail.io/v1/inbound/routes/67e4f2b6d4b7a63a0f6f2e11 \ -H "Authorization: Bearer replace-with-your-api-key" ``` **Response:** ```json { "deleted": true } ``` --- ## List received emails **`GET`** `https://api.sidemail.io/v1/inbound/emails` Returns a paginated list of received inbound emails, sorted by most recent first. ```bash curl "https://api.sidemail.io/v1/inbound/emails?limit=20" \ -H "Authorization: Bearer replace-with-your-api-key" ``` **Parameters:** - `limit` (number, optional, default: `20`): Number of results to return. Min 1, max 100. - `paginationCursorNext` (string, optional): Cursor for fetching the next page of results. - `paginationCursorPrev` (string, optional): Cursor for fetching the previous page of results. **Response:** ```json { "data": [ { "id": "67e4f2b6d4b7a63a0f6f2e11", "destination": "hi@example.com", "from": { "email": "john@acme.com", "name": "John" }, "to": [{ "email": "hi@example.com", "name": "Support" }], "cc": [], "replyTo": [], "subject": "Invoice Q1", "receivedAt": "2026-03-27T12:01:18.921Z", "spam": { "score": 2.4 }, "inboundRouteId": "67e4f2b6d4b7a63a0f6f2e10" } ], "hasMore": false, "paginationCursorNext": null, "paginationCursorPrev": null, "limit": 20 } ``` The list endpoint returns a summary of each email. To get the full email detail including body, attachments, and spam analysis, use the [retrieve endpoint](#retrieve-a-received-email). --- ## Retrieve a received email **`GET`** `https://api.sidemail.io/v1/inbound/emails/:receivedEmailId` Returns the full detail of a single received email, including text body, attachment metadata, spam analysis, and short-lived preview/download URLs. ```bash curl https://api.sidemail.io/v1/inbound/emails/67e4f2b6d4b7a63a0f6f2e11 \ -H "Authorization: Bearer replace-with-your-api-key" ``` **Response:** ```json { "id": "67e4f2b6d4b7a63a0f6f2e11", "destination": "hi@example.com", "from": { "email": "john@acme.com", "name": "John" }, "to": [{ "email": "hi@example.com", "name": "Support" }], "cc": [{ "email": "ops@example.com", "name": null }], "replyTo": [{ "email": "billing@acme.com", "name": "Billing" }], "subject": "Invoice Q1", "text": "Hello, please find invoice attached...", "htmlAvailable": true, "attachments": [ { "name": "invoice-q1.pdf", "contentType": "application/pdf", "size": 84320 } ], "headers": {}, "auth": {}, "envelope": {}, "inboundRouteId": "67e4f2b6d4b7a63a0f6f2e10", "spam": { "score": 2.4, "threshold": 15, "isSpam": false, "action": "accept", "symbols": ["R_SPF_ALLOW", "R_DKIM_ALLOW", "MIME_GOOD"] }, "receivedAt": "2026-03-27T12:01:18.921Z", "previewHtmlUrl": "https://signed-url.example/...", "rawEmailUrl": "https://signed-url.example/..." } ``` `previewHtmlUrl` and `rawEmailUrl` are short-lived. Fetch fresh URLs by calling this endpoint again when needed. To access attachment content, download the raw email via `rawEmailUrl` and parse it with a MIME parser (e.g., `mailparser` for Node.js). The `attachments` array in the response contains metadata only (name, content type, size in bytes). --- ## Webhooks When an inbound route receives an email, Sidemail fires an `email.received` webhook event. See the [webhooks documentation](/docs/webhooks/) for setup instructions and the full event payload. --- # Messenger API Source: https://sidemail.io/docs/api/messenger/index.md # Messenger API methods (unstable) Sidemail Messenger API lets you easily send newsletters, campaigns, and product updates to your users — fully automated, personalized, and managed via API. Create and schedule one-time broadcasts, target any segment of contacts. ### Available API endpoints: - `GET https://api.sidemail.io/v1/messenger` — List all Messenger drafts - `GET https://api.sidemail.io/v1/messenger/{id}` — Get a Messenger draft - `POST https://api.sidemail.io/v1/messenger` — Create a Messenger draft - `PATCH https://api.sidemail.io/v1/messenger/{id}` — Update a Messenger draft - `DELETE https://api.sidemail.io/v1/messenger/{id}` — Delete a Messenger draft --- ## Messenger Object A Messenger draft object represents a single message draft. - `id` (string): Unique identifier of the Messenger draft. - `subject` (string): Subject line of the Messenger. Defaults to `Untitled draft`. - `fromName` (string, optional): Name of the sender. - `fromAddress` (string): Email address of the sender. - `html` (string, optional): HTML content of the message (used if not using template). - `richTextValue` (object or null, optional): Rich text content, or null if not set. - `templateId` (string or null, optional): Template ID to use. - `templateProps` (object, optional): Template variables. Values accept the same forms as the Email API: strings, number arrays, or arrays of objects used by dynamic template components. - `recipients` (object): Recipients query object. See [Contacts API](/docs/api/contacts/#query-contacts-unstable). - `contactId` (string or array of strings, optional): List of contact IDs. - `groupId` (string or array of strings, optional): List of group IDs. - `filter` (object, optional): Advanced filter object. Its nested `filter.match` controls how rules inside the filter are combined and defaults to `"all"` ([see Contacts API](/docs/api/contacts/#query-contacts-unstable)). - `match` ("all" or "any", optional): Controls how the top-level `contactId`, `groupId`, and `filter` selectors are combined. Defaults to `"any"` when creating a draft. - `isSubscribed` (boolean, optional): Only accepts `true`, defaults to `true`. - `scheduledAt` (string or null, optional): ISO8601 date string for scheduled delivery, or null. - `isTimezoneScheduled` (boolean): Whether to schedule by recipient timezone. Defaults to `false`. - `contentType` (string): One of `"with-layout"`, `"no-layout"`, or `"template"`. Defaults to `"with-layout"`. - `status` (string): One of `"draft"`, `"queued"`, `"scheduled"`, `"processing"`, `"DONE"`. Only `"draft"` and `"queued"` statuses are editable. Defaults to `"draft"`. - `createdAt` (string): ISO8601 date string when the Messenger was created. - `stats` (object, not editable): Stats object (see below). Only defined after a draft is queued. - `totalRecipients` (number): Total recipients. - `unsubscribedTotal` (number): Unsubscribed recipients. - `deliveriesTotal` (number): Delivered messages. - `complaintsTotal` (number): Complaints received. - `bouncesTotal` (number): Bounced emails. - `uniqueOpensTotal` (number): Unique opens. ### Recipient matching - `recipients.match`: Combines `contactId`, `groupId`, and `filter`. Default is `"any"` when creating a draft. - `recipients.filter.match`: Combines rules inside `filter.rules`. Default is `"all"`. - `isSubscribed`: Always applied as an additional AND condition. Messenger draft updates are partial. Recipient fields omitted from an update retain their existing values. Set unused `contactId`, `groupId`, or `filter` fields to `null` to remove them. Multiple IDs within `groupId` match contacts belonging to any of the supplied groups. A draft without `contactId`, `groupId`, or `filter` targets all subscribed contacts. ### Check the recipient count before queueing Use the [Contacts query endpoint](/docs/api/contacts/#query-contacts-unstable) with the same recipient query before queueing a Messenger draft. The response includes the number of matching contacts in `totalCount`. Example request: ```json { "isSubscribed": true, "groupId": ["5d45dd1ca546d200fe201f83"], "match": "all", "filter": { "match": "all", "rules": [ { "field": "customProps.plan", "operator": "includes", "value": "premium" } ] }, "limit": 1 } ``` Example response: ```json { "data": [ /* Array of contact objects */ ], "pageCount": 42, "totalCount": 42 } ``` Provide `match` explicitly when checking the recipient count. The Contacts query endpoint defaults `match` to `"all"`, while Messenger draft creation defaults it to `"any"`. Recipient criteria are evaluated again during delivery. Contact properties and group membership changes made after queueing may affect the final recipients. ### Example data: ```json { "id": "5d45dd1ca546d200fe201f84", "subject": "Welcome to Sidemail!", "fromName": "Sidemail Team", "fromAddress": "team@yourdomain.com", "html": "

Hello {{contact.name}}, welcome!

", "richTextValue": null, "templateId": null, "templateProps": null, "recipients": { "contactId": null, "groupId": ["5d45dd1ca546d200fe201f83"], "filter": { "match": "all", "rules": [ { "field": "customProps.plan", "operator": "includes", "value": "premium" }, { "field": "createdAt", "operator": "gte", "value": "2025-01-01T00:00:00.000Z" } ] }, "match": "all", "isSubscribed": true }, "scheduledAt": "2025-05-10T09:00:00.000Z", "isTimezoneScheduled": false, "contentType": "with-layout", "status": "DONE", "createdAt": "2025-05-04T12:00:00.000Z", "stats": { "totalRecipients": 1500, "unsubscribedTotal": 5, "deliveriesTotal": 1490, "complaintsTotal": 1, "bouncesTotal": 4, "uniqueOpensTotal": 900 } } ``` --- ## Get a Messenger draft `GET https://api.sidemail.io/v1/messenger/:id` ### Returns Example response: ```json { "data": {/* Messenger object */ } } ``` --- ## List Messenger drafts `GET https://api.sidemail.io/v1/messenger` ### Query string parameters - `offset` (number, optional): Number of drafts to skip (pagination). Default: `0`. - `limit` (number, optional): Number of drafts per page. Default: `20`. ### Returns Example response: ```json { "data": [/* ... */], "pageCount": 5, "totalCount": 42 } ``` --- ## Create a Messenger draft `POST https://api.sidemail.io/v1/messenger` The request body must be a valid Messenger draft object ([see above](#messenger-object)) Example request: ```json { "subject": "Welcome!", "fromAddress": "team@yourdomain.com", "templateId": "5d45dd1ca546d200fe201f83", "templateProps": { "firstName": "Patrik", "metrics": [12, 19, 7], "items": [ { "name": "Pro plan", "links": [{ "label": "Open dashboard", "url": "https://example.com" }] } ] }, "recipients": { "groupId": ["5d45dd1ca546d200fe201f83"], "match": "all", "filter": { "match": "all", "rules": [ { "field": "customProps.plan", "operator": "includes", "value": "premium" } ] } }, "scheduledAt": "2025-05-04T12:00:00.000Z" } ``` ### Returns Returns the created Messenger draft object ([see above](#messenger-object)). --- ## Update a Messenger draft `PATCH https://api.sidemail.io/v1/messenger/:id` The request body must be a valid Messenger draft object ([see above](#messenger-object)). Partial updates are supported. Recipient fields omitted from an update retain their existing saved values. When changing recipients, explicitly provide top-level `recipients.match` and set unused selectors to `null`. Example request: ```json { "subject": "Updated subject", "scheduledAt": "2025-05-05T12:00:00.000Z" } ``` Example to queue a draft for delivery: ```json { "status": "queued" } ``` Setting the status to `"queued"` begins the delivery process without an additional API confirmation prompt. Verify the saved recipient settings and expected recipient count before queueing. ### Returns Returns status code 200 on success and an empty JSON object: ```json {} ``` --- ## Delete a Messenger draft `DELETE https://api.sidemail.io/v1/messenger/:id` Permanently deletes a Messenger draft. It cannot be undone. ### Returns Example response: ```json { "deleted": true } ``` --- # Linked projects Source: https://sidemail.io/docs/api/projects/index.md # Linked projects API methods Linked projects enable white-label sending through Sidemail. They are useful when you need a separate sending identity for each customer, tenant, or user in your application. Each linked project has access to the templates of the parent project (via template ID, not template names). We recommend creating one linked project per user or tenant for optimal white-label functionality. Each linked project can have its own bounce and complaint reputation limits. Organization-level health limits still apply globally, so one linked project can be paused without bypassing the parent organization's overall sending health. ### Available API endpoints: - `POST https://api.sidemail.io/v1/project` - `GET https://api.sidemail.io/v1/project` - `PATCH https://api.sidemail.io/v1/project` - `DELETE https://api.sidemail.io/v1/project` ## Using linked projects (example) This outlines how a linked project is designed for white-label email delivery: 1. Create a linked project for each user of your application. 2. Customize the email template design by updating the linked project. 3. Send emails through linked projects using their respective API keys. ```js import { configureSidemail } from "@sidemail/sidemail"; // 1) Configure with your main project API key const sidemail = configureSidemail({ apiKey: "replace-with-main-project-api-key" }); // 2) Create a linked project and store its apiKey const createRes = await sidemail.project.create({ name: "Customer X linked project" }); const linkedApiKey = createRes.apiKey; // save for future requests // 3) Update design within the linked project context const sidemailLinked = configureSidemail({ apiKey: linkedApiKey }); await sidemailLinked.project.update({ name: "New name", emailTemplateDesign: { logo: { sizeWidth: 50, href: "https://example.com", file: "PHN2ZyBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjIiIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMTIgNS43MmMtMi42MjQtNC41MTctMTAtMy4xOTgtMTAgMi40NjEgMCAzLjcyNSA0LjM0NSA3LjcyNyA5LjMwMyAxMi41NC4xOTQuMTg5LjQ0Ni4yODMuNjk3LjI4M3MuNTAzLS4wOTQuNjk3LS4yODNjNC45NzctNC44MzEgOS4zMDMtOC44MTQgOS4zMDMtMTIuNTQgMC01LjY3OC03LjM5Ni02Ljk0NC0xMC0yLjQ2MXoiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==", }, font: { name: "Acme" }, colors: { highlight: "#0000FF", isDarkModeEnabled: true }, unsubscribeText: "Darse de baja", footerTextTransactional: "You're receiving these emails because you registered for Acme Inc.", }, }); // 4) Get the linked project const getRes = await sidemailLinked.project.get(); // 5) Send an email using a parent templateId await sidemailLinked.sendEmail({ toAddress: "user@email.com", fromAddress: "you@example.com", fromName: "Your app", templateId: "65dc4595af2e7a530209b414", }); ``` ```ruby require "sidemail" # 1) Configure with your main project API key sm = Sidemail.new(api_key: "replace-with-main-project-api-key") # 2) Create a linked project and store its apiKey create_res = sm.project.create(name: "Customer X linked project") linked_api_key = create_res.api_key # save for future requests # 3) Update design within the linked project context sm_linked = Sidemail.new(api_key: linked_api_key) sm_linked.project.update( name: "New name", emailTemplateDesign: { logo: { sizeWidth: 50, href: "https://example.com", file: "PHN2ZyBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjIiIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMTIgNS43MmMtMi42MjQtNC41MTctMTAtMy4xOTgtMTAgMi40NjEgMCAzLjcyNSA0LjM0NSA3LjcyNyA5LjMwMyAxMi41NC4xOTQuMTg5LjQ0Ni4yODMuNjk3LjI4M3MuNTAzLS4wOTQuNjk3LS4yODNjNC45NzctNC44MzEgOS4zMDMtOC44MTQgOS4zMDMtMTIuNTQgMC01LjY3OC03LjM5Ni02Ljk0NC0xMC0yLjQ2MXoiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==", }, font: { name: "Acme" }, colors: { highlight: "#0000FF", isDarkModeEnabled: true }, unsubscribeText: "Darse de baja", footerTextTransactional: "You're receiving these emails because you registered for Acme Inc.", } ) # 4) Get the linked project get_res = sm_linked.project.get # 5) Send an email using a parent templateId sm_linked.send_email( toAddress: "user@email.com", fromAddress: "you@example.com", fromName: "Your app", templateId: "65dc4595af2e7a530209b414" ) ``` ```php project->create([ 'name' => 'Customer X linked project', ]); $linkedApiKey = $createRes->apiKey; // save for future requests // 3) Update design within the linked project context $sidemailLinked = new Sidemail(apiKey: $linkedApiKey); $sidemailLinked->project->update([ 'name' => 'New name', 'emailTemplateDesign' => [ 'logo' => [ 'sizeWidth' => 50, 'href' => 'https://example.com', 'file' => 'PHN2ZyBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjIiIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMTIgNS43MmMtMi42MjQtNC41MTctMTAtMy4xOTgtMTAgMi40NjEgMCAzLjcyNSA0LjM0NSA3LjcyNyA5LjMwMyAxMi41NC4xOTQuMTg5LjQ0Ni4yODMuNjk3LjI4M3MuNTAzLS4wOTQuNjk3LS4yODNjNC45NzctNC44MzEgOS4zMDMtOC44MTQgOS4zMDMtMTIuNTQgMC01LjY3OC03LjM5Ni02Ljk0NC0xMC0yLjQ2MXoiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==', ], 'font' => ['name' => 'Acme'], 'colors' => ['highlight' => '#0000FF', 'isDarkModeEnabled' => true], 'unsubscribeText' => 'Darse de baja', 'footerTextTransactional' => "You're receiving these emails because you registered for Acme Inc.", ], ]); // 4) Get the linked project $getRes = $sidemailLinked->project->get(); // 5) Send an email using a parent templateId $sidemailLinked->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'templateId' => '65dc4595af2e7a530209b414', ]); ``` ```python from sidemail import Sidemail # 1) Configure with your main project API key sm = Sidemail(api_key="replace-with-main-project-api-key") # 2) Create a linked project and store its apiKey project = sm.project.create(name="Customer X linked project") linked_api_key = project.apiKey # save for future requests # 3) Update design within the linked project context sm_linked = Sidemail(api_key=linked_api_key) sm_linked.project.update( name="New name", emailTemplateDesign={ "logo": { "sizeWidth": 50, "href": "https://example.com", "file": "PHN2ZyBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjIiIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMTIgNS43MmMtMi42MjQtNC41MTctMTAtMy4xOTgtMTAgMi40NjEgMCAzLjcyNSA0LjM0NSA3LjcyNyA5LjMwMyAxMi41NC4xOTQuMTg5LjQ0Ni4yODMuNjk3LjI4M3MuNTAzLS4wOTQuNjk3LS4yODNjNC45NzctNC44MzEgOS4zMDMtOC44MTQgOS4zMDMtMTIuNTQgMC01LjY3OC03LjM5Ni02Ljk0NC0xMC0yLjQ2MXoiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==", }, "font": {"name": "Acme"}, "colors": {"highlight": "#0000FF", "isDarkModeEnabled": True}, "unsubscribeText": "Darse de baja", "footerTextTransactional": "You're receiving these emails because you registered for Acme Inc.", }, ) # 4) Get the linked project get_res = sm_linked.project.get() # 5) Send an email using a parent templateId sm_linked.send_email( toAddress="user@email.com", fromAddress="you@example.com", fromName="Your app", templateId="65dc4595af2e7a530209b414", ) ``` ## Create a linked project A linked project is automatically associated with a regular project using the provided API key in the request. To personalize the email template design, make a subsequent update API request. Linked projects will be visible within the parent project on the API page in your Sidemail dashboard. It's crucial to save the `apiKey` provided in the response, as it is required for subsequent API requests related to this linked project. `POST https://api.sidemail.io/v1/project` Example: ```javascript { "name": "Lorem ipsum", "bounceRateLimit": 7, "complaintRateLimit": 0.1 } ``` ```js import { configureSidemail } from "@sidemail/sidemail"; const sidemail = configureSidemail({ apiKey: "replace-with-main-project-api-key" }); const res = await sidemail.project.create({ name: "Lorem ipsum", bounceRateLimit: 7, complaintRateLimit: 0.1, }); // Store res.apiKey for future calls scoped to this linked project ``` ```php project->create([ 'name' => 'Lorem ipsum', 'bounceRateLimit' => 7, 'complaintRateLimit' => 0.1, ]); // Important! Save $res->apiKey for later use ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-main-project-api-key") project = sm.project.create( name="Lorem ipsum", bounceRateLimit=7, complaintRateLimit=0.1, ) # Important! Save project.apiKey for later use ``` ### Parameters **name** `string`\ Name of the linked project as displayed in the Sidemail dashboard. --- **bounceRateLimit** `number` Optional maximum adjusted bounce rate allowed for this linked project, expressed as a percentage. For example, `7` means 7%. Minimum `0.1`, maximum `10`. If omitted, Sidemail uses the account default. --- **complaintRateLimit** `number` Optional maximum adjusted complaint rate allowed for this linked project, expressed as a percentage. For example, `0.1` means 0.1%. Minimum `0.001`, maximum is your account's complaint-rate limit. If omitted, Sidemail uses the account default. --- ### Returns ```javascript { "created": { "id": "65d75fea9cca1727dc110e1b", "apiKey": "TcL6gLeiok8KiQf1ggqkfiA6n888w4r4i0cSqeEM", "bounceRateLimit": 7, "complaintRateLimit": 0.1 } } ``` **id** `string`\ Unique identifier of the linked project. --- **apiKey** `string`\ Store this value for subsequent API requests related to this linked project (provided only once). --- **bounceRateLimit** `number` The configured bounce rate limit for this linked project. --- **complaintRateLimit** `number` The configured complaint rate limit for this linked project. ## Update a linked project Updates a linked project based on the provided API key in the `Authorization` header. `PATCH https://api.sidemail.io/v1/project` Example: ```javascript { "name": "New name", "bounceRateLimit": 7, "complaintRateLimit": 0.1, "emailTemplateDesign": { "logo": { "sizeWidth": 50, "href": "https://example.com", "file": "PHN2ZyBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjIiIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtMTIgNS43MmMtMi42MjQtNC41MTctMTAtMy4xOTgtMTAgMi40NjEgMCAzLjcyNSA0LjM0NSA3LjcyNyA5LjMwMyAxMi41NC4xOTQuMTg5LjQ0Ni4yODMuNjk3LjI4M3MuNTAzLS4wOTQuNjk3LS4yODNjNC45NzctNC44MzEgOS4zMDMtOC44MTQgOS4zMDMtMTIuNTQgMC01LjY3OC03LjM5Ni02Ljk0NC0xMC0yLjQ2MXoiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==" }, "font": { "name": "Acme" }, "colors": { "highlight": "#0000FF", "isDarkModeEnabled": true }, "unsubscribeText": "Darse de baja", "footerTextTransactional": "You're receiving these emails because you registered for Acme Inc." } } ``` ```js import { configureSidemail } from "@sidemail/sidemail"; // Use the linked project's apiKey for updates const sidemail = configureSidemail({ apiKey: "replace-with-linked-project-api-key" }); await sidemail.project.update({ name: "New name", bounceRateLimit: 7, complaintRateLimit: 0.1, emailTemplateDesign: { logo: { sizeWidth: 50, href: "https://example.com", file: "...base64..." }, font: { name: "Acme" }, colors: { highlight: "#0000FF", isDarkModeEnabled: true }, unsubscribeText: "Darse de baja", footerTextTransactional: "You're receiving these emails because you registered for Acme Inc.", }, }); ``` ```php project->update([ 'name' => 'New name', 'bounceRateLimit' => 7, 'complaintRateLimit' => 0.1, 'emailTemplateDesign' => [ 'logo' => [ 'sizeWidth' => 50, 'href' => 'https://example.com', 'file' => '...base64...', ], 'font' => ['name' => 'Acme'], 'colors' => ['highlight' => '#0000FF', 'isDarkModeEnabled' => true], 'unsubscribeText' => 'Darse de baja', 'footerTextTransactional' => "You're receiving these emails because you registered for Acme Inc.", ], ]); ``` ```python from sidemail import Sidemail # Use the linked project's apiKey for updates sm = Sidemail(api_key="replace-with-linked-project-api-key") sm.project.update( name="New name", bounceRateLimit=7, complaintRateLimit=0.1, emailTemplateDesign={ "logo": {"sizeWidth": 50, "href": "https://example.com", "file": "...base64..."}, "font": {"name": "Acme"}, "colors": {"highlight": "#0000FF", "isDarkModeEnabled": True}, "unsubscribeText": "Darse de baja", "footerTextTransactional": "You're receiving these emails because you registered for Acme Inc.", }, ) ``` ### Parameters **name** `string`\ The name of the linked project for your reference in the Sidemail dashboard. --- **bounceRateLimit** `number` Maximum adjusted bounce rate allowed for this linked project, expressed as a percentage. For example, `7` means 7%. Minimum `0.1`, maximum `10`. --- **complaintRateLimit** `number` Maximum adjusted complaint rate allowed for this linked project, expressed as a percentage. For example, `0.1` means 0.1%. Minimum `0.001`, maximum is your account's complaint-rate limit. --- **isSendingPaused** `boolean` Set to `true` to pause sending for this linked project. Set to `false` to re-enable linked-project sending. Only linked projects can be paused or re-enabled with this field. --- **emailTemplateDesign** `object`\ The design of the email template, with its properties described below. --- ### Email template design object **font** `object`\ The primary font for the email template. Font object properties are described below. --- **fontFallback** `object`\ Fallback font for email templates on clients that don't support custom fonts. Font fallback object properties are described below. --- **colors** `object`\ Color scheme for the email template. Colors object properties are described below. --- **logo** `object`\ The logo of the email design. Logo object properties are described below. --- **unsubscribeText** `string`\ The text of the unsubscribe link displayed at the bottom of the email template. --- **unsubscribePageTitle** `string`\ The title shown on the unsubscribe page. --- **unsubscribePageDescription** `string`\ The description shown on the unsubscribe page. --- **footerTextPromotional** `string`\ The footer text for promotional emails shown at the bottom of the email template. Some markdown formatting is supported: hyperlink, bold, italics, strikethrough. Variables are supported via `{variable}` syntax. --- **footerTextTransactional** `string`\ The footer text for transactional emails shown at the bottom of the email template. Some markdown formatting is supported: hyperlink, bold, italics, strikethrough. Variables are supported via `{variable}` syntax. --- ### Font object **name** `string`\ The name of a specific font. Supported fonts (the name must match exactly): ``` sans-serif, serif, monospace, ABeeZee, Abel, Abhaya Libre, Abril Fatface, Aclonica, Acme, Actor, Adamina, Advent Pro, Aguafina Script, Akronim, Aladin, Alata, Alatsi, Aldrich, Alef, Alegreya, Alegreya SC, Alegreya Sans, Alegreya Sans SC, Aleo, Alex Brush, Alfa Slab One, Alice, Alike, Alike Angular, Allan, Allerta, Allerta Stencil, Allura, Almarai, Almendra, Almendra Display, Almendra SC, Amarante, Amaranth, Amatic SC, Amethysta, Amiko, Amiri, Amita, Anaheim, Andada, Andika, Angkor, Annie Use Your Telescope, Anonymous Pro, Antic, Antic Didone, Antic Slab, Anton, Arapey, Arbutus, Arbutus Slab, Architects Daughter, Archivo, Archivo Black, Archivo Narrow, Aref Ruqaa, Arima Madurai, Arimo, Arizonia, Armata, Arsenal, Artifika, Arvo, Arya, Asap, Asap Condensed, Asar, Asset, Assistant, Astloch, Asul, Athiti, Atma, Atomic Age, Aubrey, Audiowide, Autour One, Average, Average Sans, Averia Gruesa Libre, Averia Libre, Averia Sans Libre, Averia Serif Libre, B612, B612 Mono, Bad Script, Bahiana, Bahianita, Bai Jamjuree, Baloo 2, Baloo Bhai 2, Baloo Bhaina 2, Baloo Chettan 2, Baloo Da 2, Baloo Paaji 2, Baloo Tamma 2, Baloo Tammudu 2, Baloo Thambi 2, Balthazar, Bangers, Barlow, Barlow Condensed, Barlow Semi Condensed, Barriecito, Barrio, Basic, Baskervville, Battambang, Baumans, Bayon, Be Vietnam, Bebas Neue, Belgrano, Bellefair, Belleza, Bellota, Bellota Text, BenchNine, Bentham, Berkshire Swash, Beth Ellen, Bevan, Big Shoulders Display, Big Shoulders Text, Bigelow Rules, Bigshot One, Bilbo, Bilbo Swash Caps, BioRhyme, BioRhyme Expanded, Biryani, Bitter, Black And White Picture, Black Han Sans, Black Ops One, Blinker, Bokor, Bonbon, Boogaloo, Bowlby One, Bowlby One SC, Brawler, Bree Serif, Bubblegum Sans, Bubbler One, Buda, Buenard, Bungee, Bungee Hairline, Bungee Inline, Bungee Outline, Bungee Shade, Butcherman, Butterfly Kids, Cabin, Cabin Condensed, Cabin Sketch, Caesar Dressing, Cagliostro, Cairo, Caladea, Calistoga, Calligraffitti, Cambay, Cambo, Candal, Cantarell, Cantata One, Cantora One, Capriola, Cardo, Carme, Carrois Gothic, Carrois Gothic SC, Carter One, Catamaran, Caudex, Caveat, Caveat Brush, Cedarville Cursive, Ceviche One, Chakra Petch, Changa, Changa One, Chango, Charm, Charmonman, Chathura, Chau Philomene One, Chela One, Chelsea Market, Chenla, Cherry Cream Soda, Cherry Swash, Chewy, Chicle, Chilanka, Chivo, Chonburi, Cinzel, Cinzel Decorative, Clicker Script, Coda, Coda Caption, Codystar, Coiny, Combo, Comfortaa, Comic Neue, Coming Soon, Concert One, Condiment, Content, Contrail One, Convergence, Cookie, Copse, Corben, Cormorant, Cormorant Garamond, Cormorant Infant, Cormorant SC, Cormorant Unicase, Cormorant Upright, Courgette, Courier Prime, Cousine, Coustard, Covered By Your Grace, Crafty Girls, Creepster, Crete Round, Crimson Pro, Crimson Text, Croissant One, Crushed, Cuprum, Cute Font, Cutive, Cutive Mono, DM Sans, DM Serif Display, DM Serif Text, Damion, Dancing Script, Dangrek, Darker Grotesque, David Libre, Dawning of a New Day, Days One, Dekko, Delius, Delius Swash Caps, Delius Unicase, Della Respira, Denk One, Devonshire, Dhurjati, Didact Gothic, Diplomata, Diplomata SC, Do Hyeon, Dokdo, Domine, Donegal One, Doppio One, Dorsa, Dosis, Dr Sugiyama, Duru Sans, Dynalight, EB Garamond, Eagle Lake, East Sea Dokdo, Eater, Economica, Eczar, El Messiri, Electrolize, Elsie, Elsie Swash Caps, Emblema One, Emilys Candy, Encode Sans, Encode Sans Condensed, Encode Sans Expanded, Encode Sans Semi Condensed, Encode Sans Semi Expanded, Engagement, Englebert, Enriqueta, Erica One, Esteban, Euphoria Script, Ewert, Exo, Exo 2, Expletus Sans, Fahkwang, Fanwood Text, Farro, Farsan, Fascinate, Fascinate Inline, Faster One, Fasthand, Fauna One, Faustina, Federant, Federo, Felipa, Fenix, Finger Paint, Fira Code, Fira Mono, Fira Sans, Fira Sans Condensed, Fira Sans Extra Condensed, Fjalla One, Fjord One, Flamenco, Flavors, Fondamento, Fontdiner Swanky, Forum, Francois One, Frank Ruhl Libre, Freckle Face, Fredericka the Great, Fredoka One, Freehand, Fresca, Frijole, Fruktur, Fugaz One, GFS Didot, GFS Neohellenic, Gabriela, Gaegu, Gafata, Galada, Galdeano, Galindo, Gamja Flower, Gayathri, Gelasio, Gentium Basic, Gentium Book Basic, Geo, Geostar, Geostar Fill, Germania One, Gidugu, Gilda Display, Girassol, Give You Glory, Glass Antiqua, Glegoo, Gloria Hallelujah, Goblin One, Gochi Hand, Gorditas, Gothic A1, Gotu, Goudy Bookletter 1911, Graduate, Grand Hotel, Gravitas One, Great Vibes, Grenze, Griffy, Gruppo, Gudea, Gugi, Gupter, Gurajada, Habibi, Halant, Hammersmith One, Hanalei, Hanalei Fill, Handlee, Hanuman, Happy Monkey, Harmattan, Headland One, Heebo, Henny Penny, Hepta Slab, Herr Von Muellerhoff, Hi Melody, Hind, Hind Guntur, Hind Madurai, Hind Siliguri, Hind Vadodara, Holtwood One SC, Homemade Apple, Homenaje, IBM Plex Mono, IBM Plex Sans, IBM Plex Sans Condensed, IBM Plex Serif, IM Fell DW Pica, IM Fell DW Pica SC, IM Fell Double Pica, IM Fell Double Pica SC, IM Fell English, IM Fell English SC, IM Fell French Canon, IM Fell French Canon SC, IM Fell Great Primer, IM Fell Great Primer SC, Ibarra Real Nova, Iceberg, Iceland, Imprima, Inconsolata, Inder, Indie Flower, Inika, Inknut Antiqua, Inria Sans, Inria Serif, Inter, Irish Grover, Istok Web, Italiana, Italianno, Itim, Jacques Francois, Jacques Francois Shadow, Jaldi, Jim Nightshade, Jockey One, Jolly Lodger, Jomhuria, Jomolhari, Josefin Sans, Josefin Slab, Jost, Joti One, Jua, Judson, Julee, Julius Sans One, Junge, Jura, Just Another Hand, Just Me Again Down Here, K2D, Kadwa, Kalam, Kameron, Kanit, Kantumruy, Karla, Karma, Katibeh, Kaushan Script, Kavivanar, Kavoon, Kdam Thmor, Keania One, Kelly Slab, Kenia, Khand, Khmer, Khula, Kirang Haerang, Kite One, Knewave, KoHo, Kodchasan, Kosugi, Kosugi Maru, Kotta One, Koulen, Kranky, Kreon, Kristi, Krona One, Krub, Kulim Park, Kumar One, Kumar One Outline, Kurale, La Belle Aurore, Lacquer, Laila, Lakki Reddy, Lalezar, Lancelot, Lateef, Lato, League Script, Leckerli One, Ledger, Lekton, Lemon, Lemonada, Lexend Deca, Lexend Exa, Lexend Giga, Lexend Mega, Lexend Peta, Lexend Tera, Lexend Zetta, Libre Barcode 128, Libre Barcode 128 Text, Libre Barcode 39, Libre Barcode 39 Extended, Libre Barcode 39 Extended Text, Libre Barcode 39 Text, Libre Baskerville, Libre Caslon Display, Libre Caslon Text, Libre Franklin, Life Savers, Lilita One, Lily Script One, Limelight, Linden Hill, Literata, Liu Jian Mao Cao, Livvic, Lobster, Lobster Two, Londrina Outline, Londrina Shadow, Londrina Sketch, Londrina Solid, Long Cang, Lora, Love Ya Like A Sister, Loved by the King, Lovers Quarrel, Luckiest Guy, Lusitana, Lustria, M PLUS 1p, M PLUS Rounded 1c, Ma Shan Zheng, Macondo, Macondo Swash Caps, Mada, Magra, Maiden Orange, Maitree, Major Mono Display, Mako, Mali, Mallanna, Mandali, Manjari, Manrope, Mansalva, Manuale, Marcellus, Marcellus SC, Marck Script, Margarine, Markazi Text, Marko One, Marmelad, Martel, Martel Sans, Marvel, Mate, Mate SC, Maven Pro, McLaren, Meddon, MedievalSharp, Medula One, Meera Inimai, Megrim, Meie Script, Merienda, Merienda One, Merriweather, Merriweather Sans, Metal, Metal Mania, Metamorphous, Metrophobic, Michroma, Milonga, Miltonian, Miltonian Tattoo, Mina, Miniver, Miriam Libre, Mirza, Miss Fajardose, Mitr, Modak, Modern Antiqua, Mogra, Molengo, Molle, Monda, Monofett, Monoton, Monsieur La Doulaise, Montaga, Montez, Montserrat, Montserrat Alternates, Montserrat Subrayada, Moul, Moulpali, Mountains of Christmas, Mouse Memoirs, Mr Bedfort, Mr Dafoe, Mr De Haviland, Mrs Saint Delafield, Mrs Sheppards, Mukta, Mukta Mahee, Mukta Malar, Mukta Vaani, Muli, Mystery Quest, NTR, Nanum Brush Script, Nanum Gothic, Nanum Gothic Coding, Nanum Myeongjo, Nanum Pen Script, Neucha, Neuton, New Rocker, News Cycle, Niconne, Niramit, Nixie One, Nobile, Nokora, Norican, Nosifer, Notable, Nothing You Could Do, Noticia Text, Noto Sans, Noto Sans HK, Noto Sans JP, Noto Sans KR, Noto Sans SC, Noto Sans TC, Noto Serif, Noto Serif JP, Noto Serif KR, Noto Serif SC, Noto Serif TC, Nova Cut, Nova Flat, Nova Mono, Nova Oval, Nova Round, Nova Script, Nova Slim, Nova Square, Numans, Nunito, Nunito Sans, Odibee Sans, Odor Mean Chey, Offside, Old Standard TT, Oldenburg, Oleo Script, Oleo Script Swash Caps, Open Sans, Open Sans Condensed, Oranienbaum, Orbitron, Oregano, Orienta, Original Surfer, Oswald, Over the Rainbow, Overlock, Overlock SC, Overpass, Overpass Mono, Ovo, Oxanium, Oxygen, Oxygen Mono, PT Mono, PT Sans, PT Sans Caption, PT Sans Narrow, PT Serif, PT Serif Caption, Pacifico, Padauk, Palanquin, Palanquin Dark, Pangolin, Paprika, Parisienne, Passero One, Passion One, Pathway Gothic One, Patrick Hand, Patrick Hand SC, Pattaya, Patua One, Pavanam, Paytone One, Peddana, Peralta, Permanent Marker, Petit Formal Script, Petrona, Philosopher, Piedra, Pinyon Script, Pirata One, Plaster, Play, Playball, Playfair Display, Playfair Display SC, Podkova, Poiret One, Poller One, Poly, Pompiere, Pontano Sans, Poor Story, Poppins, Port Lligat Sans, Port Lligat Slab, Pragati Narrow, Prata, Preahvihear, Press Start 2P, Pridi, Princess Sofia, Prociono, Prompt, Prosto One, Proza Libre, Public Sans, Puritan, Purple Purse, Quando, Quantico, Quattrocento, Quattrocento Sans, Questrial, Quicksand, Quintessential, Qwigley, Racing Sans One, Radley, Rajdhani, Rakkas, Raleway, Raleway Dots, Ramabhadra, Ramaraja, Rambla, Rammetto One, Ranchers, Rancho, Ranga, Rasa, Rationale, Ravi Prakash, Red Hat Display, Red Hat Text, Redressed, Reem Kufi, Reenie Beanie, Revalia, Rhodium Libre, Ribeye, Ribeye Marrow, Righteous, Risque, Roboto, Roboto Condensed, Roboto Mono, Roboto Slab, Rochester, Rock Salt, Rokkitt, Romanesco, Ropa Sans, Rosario, Rosarivo, Rouge Script, Rozha One, Rubik, Rubik Mono One, Ruda, Rufina, Ruge Boogie, Ruluko, Rum Raisin, Ruslan Display, Russo One, Ruthie, Rye, Sacramento, Sahitya, Sail, Saira, Saira Condensed, Saira Extra Condensed, Saira Semi Condensed, Saira Stencil One, Salsa, Sanchez, Sancreek, Sansita, Sarabun, Sarala, Sarina, Sarpanch, Satisfy, Sawarabi Gothic, Sawarabi Mincho, Scada, Scheherazade, Schoolbell, Scope One, Seaweed Script, Secular One, Sedgwick Ave, Sedgwick Ave Display, Sen, Sevillana, Seymour One, Shadows Into Light, Shadows Into Light Two, Shanti, Share, Share Tech, Share Tech Mono, Shojumaru, Short Stack, Shrikhand, Siemreap, Sigmar One, Signika, Signika Negative, Simonetta, Single Day, Sintony, Sirin Stencil, Six Caps, Skranji, Slabo 13px, Slabo 27px, Slackey, Smokum, Smythe, Sniglet, Snippet, Snowburst One, Sofadi One, Sofia, Solway, Song Myung, Sonsie One, Sorts Mill Goudy, Source Code Pro, Source Sans Pro, Source Serif Pro, Space Mono, Spartan, Special Elite, Spectral, Spectral SC, Spicy Rice, Spinnaker, Spirax, Squada One, Sree Krushnadevaraya, Sriracha, Srisakdi, Staatliches, Stalemate, Stalinist One, Stardos Stencil, Stint Ultra Condensed, Stint Ultra Expanded, Stoke, Strait, Stylish, Sue Ellen Francisco, Suez One, Sulphur Point, Sumana, Sunflower, Sunshiney, Supermercado One, Sura, Suranna, Suravaram, Suwannaphum, Swanky and Moo Moo, Syncopate, Tajawal, Tangerine, Taprom, Tauri, Taviraj, Teko, Telex, Tenali Ramakrishna, Tenor Sans, Text Me One, Thasadith, The Girl Next Door, Tienne, Tillana, Timmana, Tinos, Titan One, Titillium Web, Tomorrow, Trade Winds, Trirong, Trocchi, Trochut, Trykker, Tulpen One, Turret Road, Ubuntu, Ubuntu Condensed, Ubuntu Mono, Ultra, Uncial Antiqua, Underdog, Unica One, UnifrakturCook, UnifrakturMaguntia, Unkempt, Unlock, Unna, VT323, Vampiro One, Varela, Varela Round, Vast Shadow, Vesper Libre, Viaoda Libre, Vibes, Vibur, Vidaloka, Viga, Voces, Volkhov, Vollkorn, Vollkorn SC, Voltaire, Waiting for the Sunrise, Wallpoet, Walter Turncoat, Warnes, Wellfleet, Wendy One, Wire One, Work Sans, Yanone Kaffeesatz, Yantramanav, Yatra One, Yellowtail, Yeon Sung, Yeseva One, Yesteryear, Yrsa, ZCOOL KuaiLe, ZCOOL QingKe HuangYou, ZCOOL XiaoWei, Zeyada, Zhi Mang Xing, Zilla Slab, Zilla Slab Highlight ``` --- **fontSize** `number`\ The default font size in pixels. Minimum 10 pixels. Maximum 30 pixels. --- **lineHeight** `number`\ The default line height in a unitless format. Minimum 1. Maximum 3. --- ### Font fallback object **name** `string`\ The name of a specific fallback font when the primary font is not supported by the email client. Supported fonts (the name must match exactly): ``` sans-serif, serif, monospace ``` --- **fontSize** `number`\ The default font size in pixels. Minimum 10 pixels. Maximum 30 pixels. --- **lineHeight** `number`\ The default line height in a unitless format. Minimum 1. Maximum 3. --- ### Colors object **highlight** `string` `hex`\ The primary highlight color used for button backgrounds and hyperlink text color. --- **bodyBg** `string` `hex`\ The body background color. --- **contentBg** `string` `hex`\ The content background color. --- **contentBorder** `string` `hex`\ The content border color. --- **buttonTextColor** `string` `hex`\ The button text color. --- **defaultTextColor** `string` `hex`\ The default text color. --- **titleTextColor** `string` `hex`\ The title color. --- **mutedTextColor** `string` `hex`\ The muted text color. --- **footerTextColor** `string` `hex`\ The footer text color. --- **isDarkModeEnabled** `boolean`\ Indicates whether the dark mode of the email template is enabled or disabled. The default is false (dark mode turned off). --- ### Logo object **file** `string` `base64`\ The logo image file in Base64 format. Use `null` to remove a previously uploaded file. --- **darkModeFile** `string` `base64`\ A logo image file in Base64 format. This logo will only be displayed in supported email clients when dark mode is enabled. Use `null` to remove a previously uploaded file. --- **placeholder** `string`\ A text logo placeholder shown when images are blocked by the email client or the logo file fails to load. --- **sizeWidth** `number`\ The width of the logo image in pixels. Minimum 0. Maximum 999. --- **href** `string`\ The URL used as a hyperlink for the logo image. Set to `null` to remove the hyperlink. Default is `null`. --- ### Returns ```javascript { "updated": { "id": "65d9cc698b6fe02d04f7f82b", "orgId": "61e6b9e048f51028dc83489f", "parentId": "65d75fea9cca1727dc110e1b", "name": "New name", "createdAt": "2024-02-22T14:53:30.004Z", "sentTotal": 0, "deliveriesTotal": 0, "bouncesTotal": 0, "complaintsTotal": 0, "suppressedTotal": 0, "bounceRateLimit": 7, "complaintRateLimit": 0.1, "sendingRate": 1, "isSendingPaused": false, "emailTemplateDesign": { "unsubscribeText": "Darse de baja", "unsubscribePageTitle": "Unsubscribed!", "unsubscribePageDescription": "You won’t receive more emails.", "font": { "name": "Acme", "type": "google", "fontSize": 15, "lineHeight": 1.6 }, "fontFallback": { "name": "sans-serif", "type": "system", "fontSize": 14, "lineHeight": 1.65 }, "colors": { "highlight": "#0000FF", "bodyBg": "#f7f7f7", "contentBg": "#ffffff", "contentBorder": "#e6e6e6", "buttonTextColor": "#ffffff", "defaultTextColor": "#575757", "titleTextColor": "#222222", "mutedTextColor": "#b3b3b3", "footerTextColor": "#b3b3b3", "isDarkModeEnabled": true }, "logo": { "sizeWidth": 50, "placeholder": "Lorem ipsum", "file": "https://sidemail.s3.amazonaws.com/user-uploaded/logos/7e06c4edd18af569e666a0c1dfef27b2", "darkModeFile": null, "href": "https://example.com" }, "footerTextPromotional": null, "footerTextTransactional": "You're receiving these emails because you registered for Acme Inc." } } } ``` **id** `string`\ Unique identifier of the linked project. --- **apiKey** `string`\ This value must be stored on your side for subsequent API requests related to this linked project (provided only once). ## Get a project Retrieves project data based on the API key in the Authorization header. This method works for both normal projects created via Sidemail dashboard and linked projects created via the API. `GET https://api.sidemail.io/v1/project` ### Parameters *No parameters.* ### Returns Returns a project object. ```javascript { "project": { "id": "65d9cc698b6fe02d04f7f82b", "orgId": "61e6b9e048f51028dc83489f", "parentId": "65d75fea9cca1727dc110e1b", "name": "Lorem ipsum", "createdAt": "2024-02-24T11:00:57.453Z", "sentTotal": 0, "deliveriesTotal": 0, "bouncesTotal": 0, "complaintsTotal": 0, "suppressedTotal": 0, "bounceRateLimit": 7, "complaintRateLimit": 0.1, "sendingRate": 1, "isSendingPaused": false, "emailTemplateDesign": { "unsubscribeText": "Unsubscribe", "unsubscribePageTitle": "Unsubscribed!", "unsubscribePageDescription": "You won’t receive more emails.", "font": { "name": "Lato", "type": "google", "fontSize": 15, "lineHeight": 1.6 }, "fontFallback": { "name": "sans-serif", "type": "system", "fontSize": 14, "lineHeight": 1.65 }, "colors": { "highlight": "#0090f0", "bodyBg": "#f7f7f7", "contentBg": "#ffffff", "contentBorder": "#e6e6e6", "buttonTextColor": "#ffffff", "defaultTextColor": "#575757", "titleTextColor": "#222222", "mutedTextColor": "#b3b3b3", "footerTextColor": "#b3b3b3" }, "logo": { "sizeWidth": 130, "placeholder": "Lorem ipsum", "url": null, "darkModeUrl": null, "href": null }, "footerTextPromotional": null, "footerTextTransactional": null } } } ``` ```js import { configureSidemail } from "@sidemail/sidemail"; // Works with either a normal project or a linked project's apiKey const sidemail = configureSidemail({ apiKey: "replace-with-project-or-linked-api-key" }); const project = await sidemail.project.get(); ``` ```php project->get(); ``` ```python from sidemail import Sidemail # Works with either a normal project or a linked project's apiKey sm = Sidemail(api_key="replace-with-project-or-linked-api-key") project = sm.project.get() ``` ## Reputation limits and sending controls Linked project bounce and complaint rates are evaluated independently from other linked projects in the same organization. The limits are configured with `bounceRateLimit` and `complaintRateLimit`, and the current configuration is returned when you create, update, or get a linked project. The dashboard shows Sidemail's adjusted reputation rate as `current / limit`. At low send volume, displayed rates may be adjusted to avoid misleading percentages. If a linked project exceeds its configured reputation limits, Sidemail can pause sending for that linked project. You can also manually pause and re-enable linked project sending from the parent project's API page in the Sidemail dashboard. You can also pause or re-enable linked-project sending via the API by updating `isSendingPaused` with the linked project's API key. ```javascript { "isSendingPaused": true } ``` ```javascript { "isSendingPaused": false } ``` Re-enabling a linked project restores linked-project sending, but organization-level health limits still apply globally and can continue to block sending. Pause and re-enable controls are available for linked projects only. They are available from the Sidemail dashboard and via `PATCH https://api.sidemail.io/v1/project`. ## Delete a linked project `DELETE https://api.sidemail.io/v1/project` Permanently deletes a linked project based on the API key in the `Authorization` header. It cannot be undone. ### Parameters *No parameters.* ### Returns Returns an object with `deleted` parameter that indicates the outcome of the operation. ```javascript { "deleted": true } ``` ```js import { configureSidemail } from "@sidemail/sidemail"; const sidemail = configureSidemail({ apiKey: "replace-with-linked-project-api-key" }); await sidemail.project.delete(); ``` ```php project->delete(); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-linked-project-api-key") resp = sm.project.delete() print(resp) ``` --- # Templates API Source: https://sidemail.io/docs/api/templates/index.md # Templates API methods (unstable) Sidemail Templates API lets you create and manage reusable email templates via API. Use templates to keep email subjects and content consistent, then send emails using a template name or ID. ### Available API endpoints: - `GET https://api.sidemail.io/v1/templates` - List templates - `GET https://api.sidemail.io/v1/templates/{id}` - Retrieve a template - `POST https://api.sidemail.io/v1/templates` - Create a template - `PATCH https://api.sidemail.io/v1/templates/{id}` - Update a template - `GET https://api.sidemail.io/v1/templates/gallery` - List gallery templates - `GET https://api.sidemail.io/v1/templates/fonts` - List available template fonts --- ## Template Object A template object represents a reusable email template. - `id` (string): Unique identifier of the template. - `name` (string): Unique template name within the project. - `subject` (string): Email subject. Template variables are supported. - `content` (array): Template content nodes. Omitted from list responses unless `includeContent` is `true`. - `emailText` (string or null, optional): Custom plain-text email content. - `preheader` (string or null, optional): Preview text displayed by supported email clients. - `sampleProps` (object or null, optional): Example template variable values shown when previewing the template in the Sidemail editor. These values are not used when sending emails. Values can be strings, arrays of non-negative numbers, or arrays of objects. - `layoutId` (string or null, optional): Identifier of the project template layout used to wrap the template. A null or unknown layout ID uses the default auto-generated layout. - `version` (number, read-only): Template content format version. Templates returned by this API use version `2`. - `createdAt` (string): ISO8601 date string when the template was created. - `updatedAt` (string, optional): ISO8601 date string when the template was last updated. ### Content nodes The `content` array contains the nodes rendered in the email. Supported node types are `text`, `button`, `list`, `table`, `divider`, `image`, `chart`, `code`, and `box`. Every content node uses these common fields: - `id` (string, optional): Unique identifier of the node. Sidemail generates an ID when it is missing or duplicated in a create or update request. - `type` (string): Node type. - `styles` (object, optional): Node style overrides. - `iterationKey` (string, optional): Name of an array in `templateProps`. The node is repeated for every item in the array. Text values, links, labels, image properties, and table cells can contain template variables using `{variable_name}` syntax. ### Node styles The fields supported inside `styles` depend on the node type. Common fields include: - `marginBottom` (number): Space below the node in pixels. - `align` (string): One of `left`, `center`, or `right`. - `width` (number or string): Width in pixels or as a percentage such as `"100%"`. - `height` (number or string): Node height. - `fontFamily`, `fontSize`, `fontWeight`, `fontStyle`, `lineHeight`, `letterSpacing`, `color`, `textTransform`, and `textDecoration`: Text style overrides. - `linkColor` and `linkTextDecoration`: Link style overrides. - `backgroundColor`, `borderWidth`, `borderColor`, and `borderRadius`: Color and border overrides. - `paddingTop`, `paddingBottom`, `paddingLeft`, and `paddingRight`: Padding in pixels. - `dark` (object): Style overrides applied in dark mode when dark mode is enabled in the project template design. - `mobile` (object): Style overrides applied on small screens. ### Text element - `text` (string): Text content. Markdown and template variables are supported. - `preset` (string, optional): Text style preset ID, such as `default`, `title`, or `muted`. ```json { "id": "text-title", "type": "text", "preset": "title", "text": "Welcome to {project_name}", "styles": { "marginBottom": 20, "align": "left" } } ``` ### Button element - `label` (string): Button label. Template variables are supported. - `url` (string): Button destination URL. Template variables are supported. - `preset` (string, optional): Button style preset ID. ```json { "id": "button-account", "type": "button", "label": "Open your account", "url": "{account_url}", "styles": { "marginBottom": 20, "align": "left" } } ``` ### List element - `items` (array of strings): List item content. Markdown and template variables are supported. - `symbol` (string, optional): List marker. Use `number` for a numbered list or an empty string to hide markers. - `preset` (string, optional): List style preset ID. ```json { "id": "list-links", "type": "list", "items": [ "Read our [documentation]({docs_url})", "Contact {support_email}" ], "symbol": "›", "styles": { "marginBottom": 20 } } ``` ### Table element - `columns` (array): Table column definitions. - `label` (string): Column heading. - `align` (string, optional): One of `left`, `center`, or `right`. - `rows` (array): Array of rows. Each row is an array of cells containing a `text` string. - `iterationTarget` (string, optional): Set to `children` together with `iterationKey` to repeat the first row for every item in the array. The default repeats the complete table. ```json { "id": "table-invoice", "type": "table", "columns": [ { "label": "Item", "align": "left" }, { "label": "Price", "align": "right" } ], "rows": [ [ { "text": "{name}" }, { "text": "{price}" } ] ], "iterationKey": "items", "iterationTarget": "children", "styles": { "marginBottom": 20 } } ``` ### Divider element The divider has no content-specific fields. Use `styles.borderWidth`, `styles.borderColor`, and `styles.borderRadius` to customize it. ```json { "id": "divider-footer", "type": "divider", "styles": { "marginBottom": 20, "borderWidth": 1, "borderColor": "#e6e6e6" } } ``` ### Image element - `src` (string): Image URL. Template variables are supported. - `darkModeSrc` (string, optional): Image URL used in dark mode. - `alt` (string): Alternative text. Template variables are supported. - `href` (string or null, optional): Link opened when the image is clicked. ```json { "id": "image-banner", "type": "image", "src": "https://example.com/banner.png", "darkModeSrc": "https://example.com/banner-dark.png", "alt": "Welcome to {project_name}", "href": "{account_url}", "styles": { "marginBottom": 20, "align": "center", "width": "100%", "borderRadius": 8 } } ``` ### Chart element - `dataKey` (string): Name of an array of non-negative numbers in `templateProps`. - `chartType` (string): One of `line` or `bar`. - `curve` (string, optional): For line charts, one of `monotone` or `linear`. - `fill` (boolean, optional): Whether to fill the chart background. - `maxValue` (number, optional): Maximum value displayed on the Y axis. - `xAxisStartLabel` and `xAxisEndLabel` (string, optional): Labels displayed at the start and end of the X axis. ```json { "id": "chart-usage", "type": "chart", "dataKey": "usage", "chartType": "line", "curve": "monotone", "fill": true, "maxValue": 100, "xAxisStartLabel": "30 days ago", "xAxisEndLabel": "Now", "styles": { "marginBottom": 20 } } ``` The matching template props would contain: ```json { "usage": [20, 35, 42, 70, 85] } ``` ### Code element - `code` (string): Code content. - `language` (string): One of `text`, `javascript`, `php`, `ruby`, `python`, or `bash`. ```json { "id": "code-install", "type": "code", "code": "npm install @sidemail/sidemail", "language": "bash", "styles": { "marginBottom": 20 } } ``` ### Box element - `columns` (array): Box columns. - `width` (string or number, optional): Column width, such as `"50%"` or `"200px"`. - `elements` (array): Content elements rendered inside the column. Elements can be nested recursively. - `styles.gap` (number, optional): Space between columns in pixels. - `styles.verticalAlign` (string, optional): One of `top`, `middle`, or `bottom`. - `styles.mobile.stackColumns` (boolean, optional): Whether columns stack on small screens. ```json { "id": "box-summary", "type": "box", "columns": [ { "width": "50%", "elements": [ { "id": "box-summary-title", "type": "text", "text": "**Current plan**" } ] }, { "width": "50%", "elements": [ { "id": "box-summary-value", "type": "text", "text": "{plan_name}" } ] } ], "styles": { "marginBottom": 20, "gap": 20, "verticalAlign": "top", "mobile": { "stackColumns": true } } } ``` ### Example Template Object ```json { "id": "65dc4595af2e7a530209b414", "name": "Welcome", "subject": "Welcome to {project_name}", "preheader": "Your account is ready.", "sampleProps": { "project_name": "Acme" }, "content": [ { "id": "i3f5n6a8q", "type": "text", "preset": "title", "text": "Welcome to {project_name}", "styles": { "marginBottom": 20 } }, { "id": "r2n9c4m7x", "type": "button", "label": "Open your account", "url": "{account_url}" } ], "emailText": null, "layoutId": "custom-layout", "version": 2, "createdAt": "2026-06-12T09:00:00.000Z", "updatedAt": "2026-06-12T09:30:00.000Z" } ``` --- ## List templates `GET https://api.sidemail.io/v1/templates` Returns templates sorted by creation date, with the most recent templates appearing first. ```bash curl -X GET "https://api.sidemail.io/v1/templates?limit=100" \ -H "Authorization: Bearer replace-with-your-api-key" ``` ### Parameters **paginationCursorNext** `string` `optional` Cursor for fetching the next page of templates. Use the `paginationCursorNext` value returned by the previous request. --- **limit** `number` `optional` Number of templates returned per page. The default and maximum value is `100`. --- **includeContent** `boolean` `optional` Whether to include the `content` array for each template. Defaults to `false` to reduce response size. ### Returns Returns a `data` property containing an array of template objects, `hasMore` indicating whether another page is available, and `paginationCursorNext` for fetching that page. Template content is omitted unless `includeContent` is `true`. ```json { "hasMore": true, "paginationCursorNext": "65dc4595af2e7a530209b413", "data": [ { "id": "65dc4595af2e7a530209b414", "name": "Welcome", "subject": "Welcome to {project_name}", "layoutId": null, "version": 2 } ] } ``` --- ## Retrieve a template `GET https://api.sidemail.io/v1/templates/{id}` Returns a template by ID. ```bash curl -X GET "https://api.sidemail.io/v1/templates/65dc4595af2e7a530209b414" \ -H "Authorization: Bearer replace-with-your-api-key" ``` ### Parameters **id** `string` Unique identifier of the template. ### Returns Returns the template under the `data` property. ```json { "data": { "id": "65dc4595af2e7a530209b414", "name": "Welcome", "subject": "Welcome to {project_name}", "content": [], "layoutId": null, "version": 2 } } ``` --- ## Create a template `POST https://api.sidemail.io/v1/templates` Creates a template. Template names must be unique. ```bash curl -X POST "https://api.sidemail.io/v1/templates" \ -H "Authorization: Bearer replace-with-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "name": "Welcome", "subject": "Welcome to {project_name}", "preheader": "Your account is ready.", "layoutId": "custom-layout", "sampleProps": { "project_name": "Acme" }, "content": [ { "type": "text", "preset": "title", "text": "Welcome to {project_name}" }, { "type": "button", "label": "Open your account", "url": "{account_url}" } ] }' ``` ### Parameters **name** `string` Required. Unique template name within the project. Maximum 200 characters. --- **subject** `string` Required. Email subject. Maximum 1,000 characters. --- **content** `array` `optional` Template content nodes. Sidemail generates IDs for nodes that do not have a unique ID. --- **emailText** `string` or `null` `optional` Custom plain-text email content. Maximum 1 MB. --- **preheader** `string` or `null` `optional` Email preview text. Maximum 200 characters. --- **sampleProps** `object` or `null` `optional` Example values for template variables shown when previewing the template in the Sidemail editor. These values are not used when sending emails. Values can be strings, arrays of non-negative numbers, or arrays of objects. --- **layoutId** `string` or `null` `optional` Identifier of the project template layout used to wrap the template. Maximum 20 characters. Use `null` or omit this field to use the default auto-generated layout. ### Returns Returns the created template under the `data` property. ```json { "data": { "id": "65dc4595af2e7a530209b414", "name": "Welcome", "subject": "Welcome to {project_name}", "content": [ { "id": "i3f5n6a8q", "type": "text", "preset": "title", "text": "Welcome to {project_name}" } ], "layoutId": "custom-layout", "version": 2, "createdAt": "2026-06-12T09:00:00.000Z" } } ``` Creating a template with an existing name returns `403 Forbidden` with the `resource-duplicate` error code. --- ## Update a template `PATCH https://api.sidemail.io/v1/templates/{id}` Updates the provided fields of a template. ```bash curl -X PATCH "https://api.sidemail.io/v1/templates/65dc4595af2e7a530209b414" \ -H "Authorization: Bearer replace-with-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "subject": "Welcome, {first_name}", "preheader": "Your account is ready." }' ``` ### Parameters **id** `string` Unique identifier of the template. The request body accepts the same template fields as the create endpoint. All fields are optional and only the provided fields are updated. ### Returns Returns the updated template under the `data` property. ```json { "data": { "id": "65dc4595af2e7a530209b414", "name": "Welcome", "subject": "Welcome, {first_name}", "preheader": "Your account is ready.", "content": [], "layoutId": "custom-layout", "version": 2, "updatedAt": "2026-06-12T09:30:00.000Z" } } ``` Updating a template to an existing name returns `403 Forbidden` with the `resource-duplicate` error code. --- ## List gallery templates `GET https://api.sidemail.io/v1/templates/gallery` Returns the templates available in the Sidemail template gallery. Gallery templates can be submitted to the create template endpoint. ```bash curl -X GET "https://api.sidemail.io/v1/templates/gallery" \ -H "Authorization: Bearer replace-with-your-api-key" ``` ### Parameters *No parameters.* ### Returns Returns gallery templates under the `data` property. Gallery templates include `slug` and `tags` metadata and do not include a saved template ID. ```json { "data": [ { "name": "Welcome", "slug": "welcome", "tags": ["transactional", "marketing"], "subject": "Welcome to {project_name}", "version": 2, "sampleProps": { "project_name": "Company name" }, "content": [ { "id": "i3f5n6a8q", "type": "text", "preset": "title", "text": "Hi there!" } ] } ] } ``` --- ## List available template fonts `GET https://api.sidemail.io/v1/templates/fonts` Returns fonts that can be used by the Sidemail template editor. ```bash curl -X GET "https://api.sidemail.io/v1/templates/fonts" \ -H "Authorization: Bearer replace-with-your-api-key" ``` ### Parameters *No parameters.* ### Returns ```json { "data": [ { "name": "Roboto", "category": "sans-serif", "variants": ["regular", "700"], "subsets": ["latin"] } ] } ``` --- # Batch email sending Source: https://sidemail.io/docs/batch-email-sending/index.md # Batch email sending Send up to 50 emails in a single API request by sending an `array` of parameters accepted by [send email method](/docs/api/email/) in a `object`. This is a recommended way to efficiently send emails to a large list of recipients. For example, if you're sending email report every week to all your users, consider batching. ## Limitations - The total size of the request must be less than 10 MB. ## Example ```javascript const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.sendEmail([ { toAddress: "user-1@example.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Weekly report", }, { toAddress: "user-2@example.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Weekly report", }, { toAddress: "user-3@example.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Weekly report", }, ]); ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ [ 'toAddress' => 'user-1@example.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'templateName' => 'Weekly report', ], [ 'toAddress' => 'user-2@example.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'templateName' => 'Weekly report', ], [ 'toAddress' => 'user-3@example.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'templateName' => 'Weekly report', ], ]); ``` ```ruby require "sidemail" sm = Sidemail.new(api_key: "replace-with-your-api-key") response = sm.send_email([ { toAddress: "user-1@example.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Weekly report", }, { toAddress: "user-2@example.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Weekly report", }, { toAddress: "user-3@example.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Weekly report", }, ]) ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.send_email([ { "toAddress": "user-1@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Weekly report", }, { "toAddress": "user-2@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Weekly report", }, { "toAddress": "user-3@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Weekly report", }, ]) ``` ```bash curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '[ { "toAddress": "user-1@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Weekly report" }, { "toAddress": "user-2@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Weekly report" }, { "toAddress": "user-3@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Weekly report" } ]' ``` ## Returns The response contains an `array` of `objects` with an email ID and a status of email on success. ```javascript [ { id: "5e858953daf20f3aac50a3da", status: "queued", }, { id: "5e858953daf20f3aac50a4da", status: "queued", }, { id: "5e858953daf20f3aac50a5da", status: "queued", }, ]; ``` --- # Quickstart to contact profiles Source: https://sidemail.io/docs/contact-profiles-quickstart/index.md # Introduction Tracking user (contact) data is useful to learn more about your users, and makes possible sending targeted emails. For example, you can track when user was last seen, how many todos completed (if todos app) or the favourite color. You can track anything, really. The more you know about your user, the better you can target them with specific emails. ## What data you should track? To get your some ideas for what data you could track about your users: - **Generic** – name, company, website, registration date - **Payment** – plan type, trial expiration date, billing interval, next billing date, customer lifetime value - **Activity** – last seen date; your application specifics, if todos app: todos created, todos archived, last todo created at date, onboarding completed date ## Setting up contact properties ### Create contact (user) properties Before you can track any user data, you need to tell Sidemail what data should expect. Head over to your project settings and find the **Contact properties** section. **There, you can create and edit properties that you track about your users.** Supported data types: - String - Number - Date [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) ### Naming convention You can name contact properties in whatever naming convention you prefer. For example, this how you could name full name property: - `fullName` - `FullName` - `full_name` - `Full name` ## Example Push contact data to Sidemail via `create or update` contact method. ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "xxxxx" }); const response = await sidemail.contacts.createOrUpdate({ emailAddress: "john.doe@example.com", identifier: "123", // ID representing the user in your database customProps: { fullName: "John doe", pricingPlan: "premium", registeredAt: "2019-08-15T13:20:39.160Z", lastSeenAt: "2019-08-20T17:40:39.160Z", // ... more of your contact data ... }, }); ``` ```ruby require "sidemail" sm = Sidemail.new(api_key: "replace-with-your-api-key") sm.contacts.create_or_update( emailAddress: "john.doe@example.com", identifier: "123", customProps: { fullName: "John doe", pricingPlan: "premium", registeredAt: "2019-08-15T13:20:39.160Z", lastSeenAt: "2019-08-20T17:40:39.160Z", } ) ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") sm.contacts.create_or_update( emailAddress="john.doe@example.com", identifier="123", customProps={ "fullName": "John doe", "pricingPlan": "premium", "registeredAt": "2019-08-15T13:20:39.160Z", "lastSeenAt": "2019-08-20T17:40:39.160Z", }, ) ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $sm->contacts->createOrUpdate([ 'emailAddress' => 'john.doe@example.com', 'identifier' => '123', 'customProps' => [ 'fullName' => 'John doe', 'pricingPlan' => 'premium', 'registeredAt' => '2019-08-15T13:20:39.160Z', 'lastSeenAt' => '2019-08-20T17:40:39.160Z', ], ]); ``` ```bash curl -X POST https://api.sidemail.io/v1/contacts \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "emailAddress": "john.doe@example.com", "identifier": "123", "customProps": { "fullName": "John doe", "pricingPlan": "premium", "registeredAt": "2019-08-15T13:20:39.160Z", "lastSeenAt": "2019-08-20T17:40:39.160Z" } }' ``` ## Force-triggering automation If you need to force trigger an automation so that all contacts currently in a matching state enter it, follow these steps: - Create a temporary custom contact property (e.g., name `trigger` and type `number`). Ensure this new property is not used in any other automation to avoid unintended re-triggers. - Go to the automation you want to force trigger, click update, and add this property as one of the triggers (e.g., `trigger` equals `1`). Save the changes. - Use the Sidemail.io API to get all contacts and update each contact with the property `trigger` set to `1`. Since your automation will have other triggers, it’s safe to update all contacts; only the correct contacts based on the automation’s triggers will enter the automation. - As a cleanup step, after you remove `trigger` as the automation trigger, make a second API update request to set the `trigger` property to `null`. This process ensures that only the relevant contacts will enter the automation while maintaining the integrity of other automations. Here's a code example: ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); let totalUpdated = 0; const result = await sidemail.contacts.list(); for await (const contact of result) { console.log(`Updating: ${contact.emailAddress} (${++totalUpdated})`); await sidemail.contacts.createOrUpdate({ emailAddress: contact.emailAddress, customProps: { trigger: 1 }, }); } console.log("All contacts processed."); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") result = sm.contacts.list() totalUpdated = 0 for contact in result.auto_paginate(): totalUpdated += 1 print(f"Updating: {contact['emailAddress']} ({totalUpdated})") sm.contacts.create_or_update( emailAddress=contact["emailAddress"], customProps={ "trigger": 1 }, ) print("All contacts processed.") ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $result = $sm->contacts->list(); $totalUpdated = 0; foreach ($result->autoPaginate() as $contact) { $totalUpdated += 1; echo "Updating: {$contact['emailAddress']} ({$totalUpdated})\n"; $sm->contacts->createOrUpdate([ 'emailAddress' => $contact['emailAddress'], 'customProps' => [ 'trigger' => 1 ], ]); } echo "All contacts processed."; ``` --- # Sending custom HTML emails Source: https://sidemail.io/docs/custom-html-emails/index.md # Sending custom HTML emails You can send custom HTML emails via the Sidemail API. To send HTML emails you'll need to specify the `html` parameter. - You can use all UTF-8 characters inside inside the `html` parameter. - An email open tracking pixel is automatically inserted at the end of the email ``. You can disable open tracking by setting parameter `isOpenTracked` to `false`. ## Example ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.sendEmail({ toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", subject: "Testing HTML only custom emails :)", html: "

Hello world! 🖐

", }); ``` ```ruby require "sidemail" sm = Sidemail.new(api_key: "replace-with-your-api-key") response = sm.send_email( toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", subject: "Testing HTML only custom emails :)", html: "

Hello world! 🖐

" ) ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ 'toAddress' => 'user@example.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'subject' => 'Testing HTML only custom emails :)', 'html' => '

Hello world! 🖐

', ]); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.send_email( toAddress="user@example.com", fromAddress="you@example.com", fromName="Your app", subject="Testing HTML only custom emails :)", html="

Hello world! 🖐

", ) ``` ```bash curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "subject": "Testing HTML only custom emails :)", "html": "

Hello world! 🖐

" }' ``` You can also send custom email with both the HTML and plain-text version. Here's an example: ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.sendEmail({ toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", subject: "Testing HTML and plain-text custom emails :)", html: "

Hello world! 🖐

", text: "Hello world! 🖐", }); ``` ```ruby require "sidemail" sm = Sidemail.new(api_key: "replace-with-your-api-key") response = sm.send_email( toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", subject: "Testing HTML and plain-text custom emails :)", html: "

Hello world! 🖐

", text: "Hello world! 🖐" ) ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ 'toAddress' => 'user@example.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'subject' => 'Testing HTML and plain-text custom emails :)', 'html' => '

Hello world! 🖐

', 'text' => 'Hello world! 🖐', ]); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.send_email( toAddress="user@example.com", fromAddress="you@example.com", fromName="Your app", subject="Testing HTML and plain-text custom emails :)", html="

Hello world! 🖐

", text="Hello world! 🖐", ) ``` ```bash curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "subject": "Testing HTML and plain-text custom emails :)", "html": "

Hello world! 🖐

", "text": "Hello world! 🖐" }' ``` --- # Sending data charts Source: https://sidemail.io/docs/data-charts/index.md # Sending data charts Sidemail no-code email editor features chart element that allows you to visualize data like time series inside of emails you send out. Include an `array` of `numbers` (must be greater than zero) inside the `templateProps` parameter to pass the data to the chart. The key name you for the chart data `array` must match the name you specified in the template element's options. ## Set up email template First, you'll need to create an template in the no-code email editor and use the chart element. You can choose either line chart or bar chart. The variable name you set up in the chart element, you'll than use in the send email method to pass data to the chart inside of `templateProps` parameter. ## Code example ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.sendEmail({ toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Weekly report", templateProps: { chart: [100, 200, 300, 400, 200, 300, 200, 500], }, }); ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ 'toAddress' => 'user@example.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'templateName' => 'Weekly report', 'templateProps' => [ 'chart' => [100, 200, 300, 400, 200, 300, 200, 500], ], ]); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.send_email( toAddress="user@example.com", fromAddress="you@example.com", fromName="Your app", templateName="Weekly report", templateProps={ "chart": [100, 200, 300, 400, 200, 300, 200, 500], } ) ``` ```bash curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Weekly report", "templateProps": { "chart": [100,200,300,400,200,300,200,500] } }' ``` --- # Sending files as email attachments Source: https://sidemail.io/docs/email-attachments/index.md # Sending files as email attachments With Sidemail.io, it's easy to send files as email attachments. To attach a file, you'll need to encode it as Base64 and put the encoded file string into the JSON body of the API request. You must also specify the file name that will show up to the recipient of the email. While attachments are supported, we recommend sending a file link instead. When sending file as email attachments, take into consideration longer deliverity times as the attached files makes the email larger and therefore slower to send, receive and process. There may be also rare cases where emails end up in spam folder due to file attachments. ```javascript const fs = require("fs"); const pdfBuffer = fs.readFileSync("./invoice.pdf"); const attachment = sidemail.fileToAttachment("invoice.pdf", pdfBuffer); await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@example.com", subject: "Invoice", text: "Invoice attached.", attachments: [attachment], }); ``` ```ruby require "sidemail" file_content = File.read("invoice.pdf") attachment = Sidemail.file_to_attachment("invoice.pdf", file_content) sm = Sidemail.new(api_key: "replace-with-your-api-key") sm.send_email( toAddress: "user@email.com", fromAddress: "you@example.com", subject: "Invoice", text: "Invoice attached.", attachments: [attachment] ) ``` ```python with open("invoice.pdf", "rb") as f: attachment = Sidemail.file_to_attachment("invoice.pdf", f.read()) sm.send_email( toAddress="user@email.com", fromAddress="you@example.com", subject="Invoice", text="Invoice attached.", attachments=[attachment], ) ``` ```php $pdfData = file_get_contents('./invoice.pdf'); $attachment = Sidemail::fileToAttachment('invoice.pdf', $pdfData); $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@example.com', 'subject' => 'Invoice', 'text' => 'Invoice attached.', 'attachments' => [$attachment], ]); ``` ```bash curl -X POST https://api.sidemail.io/v1/email/send \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@example.com", "fromAddress": "you@example.com", "subject": "Invoice", "text": "Invoice attached.", "attachments": [ { "name": "file.txt", "content": "dmFsaWQgY29udGVudA==" } ] }' ``` ## Limitations - The maximum size of all encoded attachments combined must be less than `5242880` characters long (5 MB). - Following file types are allowed: `.jpg` `.jpeg` `.pdf` `.csv` `.html` `.png` `.gif` `.json` `.txt` `.docx` `.xlsx` `.pptx`. If you need another file type allowed or have related requests: [contact us](/contact/). ## Dashboard preview You can see historically sent emails and their attachments (and download them) in your Sidemail.io dashboard (by clicking on an email in your project's sending history). ## More examples ### Send email with an attachment To send an email with attachment the JSON data in the API request should include the attachments `array` with an attachment `object` which must include name and Base64 content. For more details, read the send email [API reference](/docs/api/email/). ```javascript { "toAddress": "user@example.com", "fromAddress": "you@example.com", "fromName": "Your app name", "templateName": "Invoice", "attachments": [ { "name": "file.txt", "content": "dmFsaWQgY29udGVudA==" }, ] } ``` ### Send email with multiple attachments Note that attachment content in the following example is just for showcase and it will result in broken files if you try to send it during testing. ```javascript { "toAddress": "user@example.com", "fromAddress": "you@example.com", "fromName": "Your app name", "templateName": "Invoice", "attachments": [ { "name": "text-file.txt", "content": "dmFsaWQgY29udGVudA==" }, { "name": "image-file.jpg", "content": "dmFsaWQgY29udGVudA==" }, { "name": "document.pdf", "content": "dmFsaWQgY29udGVudA==" }, ] } ``` ### Send inline image in custom HTML To embed an image inside custom HTML, reference it with a `cid:` URL and set the same `cid` value on the attachment. The `cid` value should not include angle brackets. ```javascript { "toAddress": "user@example.com", "fromAddress": "you@example.com", "fromName": "Your app name", "subject": "Inline image example", "html": "

Hello!

", "attachments": [ { "name": "logo.png", "content": "dmFsaWQgY29udGVudA==", "cid": "logo" }, ] } ``` ### Fetch image from URL and send it as email attachment Fetch random image from Unsplash and send it as email attachment with Node.js. ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const imgBuffer = await (await fetch("https://source.unsplash.com/random")).buffer(); const response = await sidemail.sendEmail({ toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app name", templateName: "Invoice", attachments: [ { name: "unsplash-random-image.jpg", content: imgBuffer.toString("base64"), }, ] }); ``` ### Send local file as email attachment Read local file from disk and send it as email attachment with Node.js. ```js const fs = require('fs/promises'); const path = require("path"); const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const filePath = path.resolve(__dirname, "./invoice.pdf"); const encodedFile = await fs.readFile(filePath, { encoding: "base64" }); const response = await sidemail.sendEmail({ toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app name", templateName: "Invoice", attachments: [ { name: "invoice.pdf", content: encodedFile, }, ] }); ``` --- # Quickstart to email sending Source: https://sidemail.io/docs/email-sending-quickstart/index.md # Introduction Start sending transactional emails from your application in just a few easy steps with Sidemail. Designed for both the best DX and UX, Sidemail's [no-code email editor](/no-code-email-editor/), [pre-made email templates](/transactional-email-templates/), and simple [email sending HTTP API](/email-sending-api/) eliminates needless complexity and speeds up your development. Read on to learn how to send the following emails from your application: - Welcome email (onboarding email) - Password reset email - Single sign-on email (SSO email, magic login link email) - Failed payment email (dunning email) - Trial expiration - Account activation - Payment receipt - Canceled subscription --- ## No HTML email knowledge required You don't need any knowledge of HTML emails because we'll look at how to use [Sidemail's no‑code email editor](/no-code-email-editor/) to create the actual emails that you'll later send from your application via API - you'll reference the email template either by name or its ID in the API request. The big problem with email development is that you can never be quite sure if your email won't break in some email clients. The testing is costly, and maintenance is a headache. Sidemail's no-code email editor supports virtually all email clients: - Mobile: Gmail, iOS 9.3+, Android 4.4+, Spark - Web: Gmail, Outlook.com, Yahoo! Mail, AOL, Zoho Mail - Desktop: Apple Mail, Windows 10 Mail, Outlook, Thunderbird [See how we tested dark-mode support](/articles/dark-mode-in-html-email/) in email clients and became the first email provider to offer dark-mode support for our customers. --- ## Start sending emails from your application Before we start, you'll need a Sidemail account - [create it here](https://client.sidemail.io/register) if you don't have one. ### Customize the email template design `(1/3)` To customize the look of your email templates, visit your project settings. The email design you select here automatically applies to all your email templates. - Upload your company logo - Fine-tune the color combination to fit your brand. - You can also enable dark mode Looks good? Nice. That's all there's to it. ### Tweak pre-made email templates `(2/3)` Visit the email templates page inside of your Sidemail project, and you'll see that you already have some email templates. We pre-made the most commonly used transactional emails as email templates to give you the best starting point - you can use them as they're, completely change them or start from scratch, it's up to you. For example, to edit the "Password reset" email template, click on the template to show a details modal window, then, click on the `Edit` button. Now, you're looking at the no-code email editor, you can edit the content of email templates here. The no-code editor features various elements to help you structure your emails: - Text element - Button element - List element - Table element - Separator element - Image element - Code (syntax-highlighted) element - [Chart element](/docs/data-charts/) Each no-code element has some settings you can adjust, for example, vertical spacing and horizontal alignment. The Sidemail no-code email editor intentionally offers little to no configuration to shift your focus to the content. It's inspired by the idea of convention over configuration. Configure the email template's additional settings such as preheader, default subject line, or whether to automatically generate the plain-text version by clicking the "Template settings" button above the template preview. Done? Click the "Save changes" button. ### Send email template via API `(3/3)` Finally, it's time to send an email via API! To make this step easy, Sidemail generates copy‑pasteable code samples in many programming languages: - Node.js ([see on npm](https://www.npmjs.com/package/sidemail)) - Ruby - PHP - Python - cURL Find the code samples inside of the email template details modal window, for example, in your Sidemail project → goto email templates page → click on the "Password reset" template → at the bottom of the modal window, you'll see the code samples. The code samples include your Sidemail API key, so you can go ahead and copy-paste them straight into your code and see what happens! Ok, but what exactly is happening? Each code sample does the same thing - creates an HTTP request and sends data encoded as JSON to the Sidemail API endpoint. In the JSON data, you tell Sidemail details like who is the recipient, who is the email from, which email template to use. [See all available parameters](/docs/api/email/). The code samples for PHP, Ruby, and Python contain a lot of boilerplate code because the code samples use built-in HTTP libraries. If your project is already depending on a custom HTTP library that makes working with HTTP requests more productive, feel free to use it for making Sidemail API request as well. [Let us know](/contact/) in what programming language we should create a Sidemail library next. --- ## Example Here's an example to send the password reset email template. ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Password reset", templateProps: { url: "https://reset.me/123" } }); ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'templateName' => 'Password reset', 'templateProps' => [ 'url' => 'https://reset.me/123' ] ]); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.send_email( toAddress="user@email.com", fromAddress="you@example.com", fromName="Your app", templateName="Password reset", templateProps={ "url": "https://reset.me/123" } ) ``` ```bash curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@email.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Password reset", "templateProps": { "url": "https://reset.me/123" } }' ``` --- ## Next steps You tweaked the email design and learned how to send the pre-made email templates, and that's all you need to implement sending of any transactional email from your app! 🎉 We pre-made all typical transactional emails you might need as email templates, but if you need a custom one, you can simply create it yourself. Next, get ready for production: [domain verification and DKIM](/docs/sending-identities/). --- # Sending markdown emails Source: https://sidemail.io/docs/markdown-emails/index.md # Sending markdown emails Sidemail API offers an option to send email content with markdown. Sidemail automatically transforms the markdown content into pixel-perfect emails optimized for all devices and inboxes (including the problematic Outlook). Final email will be branded with your logo and customized based on your project email design. ### Markdown feature highlights: - Pixel-perfect design optimized for transactional emails - YAML-formatted metadata support (subject and from address directly in .md file) - Dark mode supported (if enabled in project design) - Open tracking supported (can be disabled) - Variables are supported with `{variable}` syntax - Button style is supported by prefixing a markdown link label with `$btn` - Code block is syntax highlighted (`js`, `php`, `ruby`, `python`, `bash`) - Responsive (optimized for the best user experience on all devices) - Won't break in Outlook and similar ## How to send transactional emails with markdown The code below is the most straightforward way to send branded transactional emails with markdown. The example showcases how to create button with `$btn` syntax, and how to include dynamic variables by using the `{variable}` syntax and putting the variable data into `templateProps`. Note that we use `trim` function to remove the access new lines which we intentionally created to make the code more readable. ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const markdown = ` Hello world, {name}! 🖐 Lorem ipsum dolor sit amet, consectetur adipiscing elit. [$btn Example button]({link}) Nam vulputate fringilla vestibulum. Nulla eu lobortis enim. Praesent varius, dui quis porta pretium, mi ex ultricies enim, sed volutpat purus erat vel nibh. `.trim(); await sidemail.sendEmail({ toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", subject: "Testing markdown emails 😊", markdown, templateProps: { name: "John", link: "https://example.com", }, }); ``` ```ruby markdown = <<~MD Hello world, {name}! 🖐 Lorem ipsum dolor sit amet, consectetur adipiscing elit. [$btn Example button]({link}) Nam vulputate fringilla vestibulum. Nulla eu lobortis enim. Praesent varius, dui quis porta pretium, mi ex ultricies enim, sed volutpat purus erat vel nibh. MD require "sidemail" sm = Sidemail.new(api_key: "replace-with-your-api-key") response = sm.send_email( toAddress: "user@email.com", fromAddress: "you@example.com", fromName: "Your app", subject: "Testing markdown emails 😊", markdown: markdown, templateProps: { name: "John", link: "https://example.com" } ) ``` ```php $markdown = <<<'MD' Hello world, {name}! 🖐 Lorem ipsum dolor sit amet, consectetur adipiscing elit. [$btn Example button]({link}) Nam vulputate fringilla vestibulum. Nulla eu lobortis enim. Praesent varius, dui quis porta pretium, mi ex ultricies enim, sed volutpat purus erat vel nibh. MD; $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'subject' => 'Testing markdown emails 😊', 'markdown' => $markdown, 'templateProps' => [ "name" => "John", "link" => "https://example.com" ] ]); ``` ```python markdown = """ Hello world, {name}! 🖐 Lorem ipsum dolor sit amet, consectetur adipiscing elit. $btn Example button Nam vulputate fringilla vestibulum. Nulla eu lobortis enim. Praesent varius, dui quis porta pretium, mi ex ultricies enim, sed volutpat purus erat vel nibh. """.strip() from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.send_email( toAddress="user@email.com", fromAddress="you@example.com", fromName="Your app", subject="Testing markdown emails 😊", markdown=markdown, templateProps={ "name": "John", "link": "https://example.com" } ) ``` ```bash MARKDOWN=$(cat <<'EOF' Hello world, {name}! 🖐 Lorem ipsum dolor sit amet, consectetur adipiscing elit. $btn Example button Nam vulputate fringilla vestibulum. Nulla eu lobortis enim. Praesent varius, dui quis porta pretium, mi ex ultricies enim, sed volutpat purus erat vel nibh. EOF ) curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@email.com", "fromAddress": "you@example.com", "fromName": "Your app", "subject": "Testing markdown emails 😊", "markdown": "'"$MARKDOWN"'", "templateProps": { "name": "John", "link": "https://example.com" } }' ``` ## Recommended setup for sending transactional emails with markdown The recommended way to use the Sidemail markdown feature, is to put the markdown email content into a separate file. For convenience, Sidemail markdown feature supports YAML-formatted metadata at the start of the markdown file. Due to the markdown metadata, the email markdown file contains all data like `subject` and `fromAddress` in one place. Currently supported properties in the YAML-formatted markdown metadata: - `subject` - `fromAddress` - `toAddress` - `replyToAddress` - `replyToName` `email.md` file example: ```` --- subject: "Testing markdown emails 😊" fromAddress: "you@example.com" fromName: "Your app" --- ## Text showcase Hello world, {name}! 🖐 Lorem ipsum dolor sit amet, consectetur adipiscing elit. [Nam vulputate fringilla vestibulum](https://example.com). Nulla eu lobortis enim. Praesent varius, dui quis porta pretium, mi ex ultricies enim, sed volutpat purus erat vel nibh. --- ## Button showcase [$btn Example button]({link}) --- ## Table showcase | Tables | Are | Cool | |----------|:-------------:|------:| | col 1 is | left-aligned | $1600 | | col 2 is | centered | $12 | | col 3 is | right-aligned | $1 | --- ## List showcase - Enter 4242 4242 4242 4242 as the card number - Enter any future date for *card expiry* - Enter any 3-digit number for CVV - Enter any **billing postal code** (90210) --- ## Code showcase Use method `sendEmail` to send email via Sidemail.io API. ```js await sidemail.sendEmail({ toAddress: "user@email.com", fromName: "Startup name", fromAddress: "your@startup.com", templateName: "Single sign-on", templateProps: { url: "https://your.app/sso?token=123" }, }); ``` ```` And to send emails based on the `email.md`, we need to import the file as UTF-8 text and pass it as the `markdown` property to the `sendEmail` function. Notice, that we don't need to include `subject`, `fromAddress` and `fromName` as that's defined in the YAML metadata in `email.md`. Here's a sample code in Node.js: ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const fs = require("fs/promises"); const path = require("path"); const markdown = await fs.readFile(path.resolve(__dirname, "./email.md"), { encoding: "utf-8", }); await sidemail.sendEmail({ toAddress: "user@example.com", markdown, templateProps: { name: "John", link: "https://example.com", }, }); ``` ## FAQ ### What markdown elements are supported? - Heading - Bold - Italic - Link - Image - Ordered list - Unordered list - Table - Code - Fenced code block - Horizontal rule ### How to apply button styles to link? You can use `$btn` indicator in markdown link label and Sidemail will automatically apply button styles to the indicated link. Also, the `$btn` indicator and access spaces will be removed from the link label, so in the example below, the link label will be "Example button label" after the transformation. ```md [\$btn Example button label](https://example.com) ``` ### How to add clickable image with link? Add image with link by adding the image as the link label. ```md [![alt text](https://source.unsplash.com/featured/500x500)](https://example.com) ``` ### Does code block support syntax highlighting? Sidemail support syntax highlighting for fenced code blocks. This feature adds color highlighting to the code inside the fenced code block. To add the syntax highlighting, specify one of the supported languages right after the initial backticks. Supported syntax highlighting annotations: js, php, ruby, python, bash ````md ```js console.log("Some javascript code"); ``` ```` ``` ### Is HTML in markdown supported? No, HTML in markdown is not supported. If you need more control, you can send custom HTML emails with Sidemail. [Learn more →](/docs/custom-html-emails/) ``` --- # MCP server Source: https://sidemail.io/docs/mcp-server/index.md # MCP Server – Send & manage emails, contacts and sending domains Sidemail MCP server enables you to programmatically send and manage transactional emails, contacts, sending domains, and Messenger features – right from MCP agent mode in VS Code, Claude, Cursor, and other MCP clients. **What is MCP?** Model Context Protocol (MCP) is a new open standard that makes it easier for AI systems to connect with external data and services. MCP acts a bit like an API for AI models – it provides a standard “language” for AI programs to access tools or data from the outside world. Learn more in our [explanation article](/articles/what-is-mcp/). ## MCP server features - Manage sending domains - Test sending transactional emails - Manage contacts and groups - Create, update, and delete Messenger drafts - Query sent emails and contacts ## Prerequisites - Node.js v18 or newer - Sidemail API Key ## Microsoft VS Code setup (assisted) 1. Open the Command Palette (`Ctrl/Cmd + Shift + P`). 2. Type **“MCP: Add Server…”** and select it. 3. Select **"NPM Package"**. 4. Enter `@sidemail/mcp` and confirm it. 5. Confirm the installation. 6. Enter your Sidemail API key and confirm it. ## Microsoft VS Code setup (manual) Alternatively, you can install it manually by modifying the `mcp.json` configuration file. 1. Open the Command Palette (`Ctrl/Cmd + Shift + P`). 2. Type **“MCP: Open User Configuration”** and select it. 3. Add the configuration below and save changes. ```json { "servers": { "sidemail-mcp": { "type": "stdio", "command": "npx", "args": ["-y", "@sidemail/mcp"], "env": { "SIDEMAIL_API_KEY": "${input:sidemail-key}" } } }, "inputs": [ { "type": "promptString", "id": "sidemail-key", "description": "Sidemail API Key", "password": true } ] } ``` ## Claude Desktop setup Edit (or create) the config file: - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` - Windows: `%APPDATA%\Claude\claude_desktop_config.json` - Linux: `~/.config/Claude/claude_desktop_config.json` ```json { "mcpServers": { "sidemail": { "command": "npx", "args": ["-y", "@sidemail/mcp"], "env": { "SIDEMAIL_API_KEY": "your-key" } } } } ``` ## Cursor setup Create or edit `~/.cursor/mcp.json`: ```json { "mcpServers": { "sidemail": { "command": "npx", "args": ["-y", "@sidemail/mcp"], "env": { "SIDEMAIL_API_KEY": "your-key" } } } } ``` ## CLI Usage ```sh npx @sidemail/mcp ``` ## MCP Server Tools ### Domains - **list-domains**: List all sending domains - **create-domain**: Add a new sending domain - **delete-domain**: Remove a sending domain *Example – Adding a new sending domain:* ### Messenger - **list-messenger-drafts**: List Messenger drafts - **get-messenger-draft**: Get Messenger draft by ID - **create-messenger-draft**: Create a Messenger draft - **update-messenger-draft**: Update a Messenger draft - **delete-messenger-draft**: Delete a Messenger draft *Example – Writing a product update email:* ### Groups - **list-groups**: List all contact groups - **create-group**: Create a new contact group - **update-group**: Update a contact group ### Contacts - **create-or-update-contact**: Create or update a contact - **query-contacts**: Query contacts with filters - **find-contact**: Find a contact by email - **delete-contact**: Delete a contact ### Emails - **send-email**: Send a transactional email (testing only) - **query-emails**: Query sent emails ## More resources - Github repository: [`sidemail/sidemail-mcp-server`](https://github.com/sidemail/sidemail-mcp-server/) - NPM package: [`@sidemail/mcp`](https://www.npmjs.com/package/@sidemail/mcp) - [Features overview](/email-mcp-server/) --- # Sending custom plain-text emails Source: https://sidemail.io/docs/plain-text-emails/index.md # Sending custom plain-text emails You can send pure plain-text emails via the Sidemail API. To send plain-text emails you'll need to specify the `text` parameter. - You can use all UTF-8 characters inside inside the `text` parameter. - Open tracking is not available for plain-text emails. ## Example ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.sendEmail({ toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", subject: "Testing plain-text only custom emails :)", text: "Hello world! 🖐", }); ``` ```ruby require "sidemail" sm = Sidemail.new(api_key: "replace-with-your-api-key") response = sm.send_email( toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", subject: "Testing plain-text only custom emails :)", text: "Hello world! 🖐" ) ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ 'toAddress' => 'user@example.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'subject' => 'Testing plain-text only custom emails :)', 'text' => 'Hello world! 🖐', ]); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.send_email( toAddress="user@example.com", fromAddress="you@example.com", fromName="Your app", subject="Testing plain-text only custom emails :)", text="Hello world! 🖐", ) ``` ```bash curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "subject": "Testing plain-text only custom emails :)", "text": "Hello world! 🖐" }' ``` --- # Scheduled email delivery Source: https://sidemail.io/docs/scheduled-email-delivery/index.md # Scheduled email delivery Schedule a delivery of an email by providing the `scheduledAt` parameter set to a valid [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date in the future. ## Example ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.sendEmail({ toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Welcome", // Deliver the email in 60 minutes from now scheduledAt: new Date(Date.now() + 60 * 60000).toISOString(), }); ``` ```ruby require "sidemail" require "date" sm = Sidemail.new(api_key: "replace-with-your-api-key") # Deliver the email in 60 minutes from now scheduled_at = (Time.now + 60 * 60).utc.iso8601 response = sm.send_email( toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Welcome", scheduledAt: scheduled_at ) ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ 'toAddress' => 'user@example.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'templateName' => 'Welcome', // Deliver the email in 60 minutes from now 'scheduledAt' => gmdate('c', time() + 60 * 60), ]); ``` ```python from sidemail import Sidemail import datetime sm = Sidemail(api_key="replace-with-your-api-key") scheduled_at = (datetime.datetime.utcnow() + datetime.timedelta(minutes=60)) scheduled_at_iso = scheduled_at.replace(microsecond=0).isoformat() + "Z" resp = sm.send_email( toAddress="user@example.com", fromAddress="you@example.com", fromName="Your app", templateName="Welcome", scheduledAt=scheduled_at_iso, ) ``` ```bash curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Welcome", "scheduledAt": "2025-12-01T12:00:00Z" }' ``` ## Unschedule email Unschedule email delivery by deleting the email via API ([see the delete email API endpoint](/docs/api/email/#delete-email)). Or unschedule email deliver from the project's history page in your dashboard. --- # Send emails with Django Source: https://sidemail.io/docs/send-emails-with-django/index.md # Send emails with Django In this quickstart, you'll learn how to send transactional emails from your [Django](https://www.djangoproject.com/) 4 or 5 app with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and scheduled reports. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash pip install sidemail ``` ## 2. Configure API key Add your API key to `settings.py`. ```python # settings.py import os SIDEMAIL_API_KEY = os.getenv("SIDEMAIL_API_KEY") ``` ## 3. Setup client Create a shared instance of the Sidemail client. You can place this in a `services.py` file within your project or app. ```python # myapp/services.py from django.conf import settings from sidemail import Sidemail sidemail = Sidemail(api_key=settings.SIDEMAIL_API_KEY) ``` ## 4. Send a welcome email Send an email when a user registers. ```python # myapp/views.py from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from .services import sidemail import json @csrf_exempt def register(request): if request.method == 'POST': data = json.loads(request.body) email = data.get('email') name = data.get('name') # ... create user in database ... sidemail.send_email( toAddress=email, fromAddress="you@yourdomain.com", fromName="Your App", templateName="Welcome", templateProps={"firstName": name}, ) return JsonResponse({"message": "User registered"}) ``` ## 5. Send a password reset email ```python # myapp/views.py @csrf_exempt def forgot_password(request): if request.method == 'POST': data = json.loads(request.body) email = data.get('email') token = "secure-reset-token" # Generate this securely sidemail.send_email( toAddress=email, fromAddress="you@yourdomain.com", fromName="Your App", templateName="Password Reset", templateProps={ "actionUrl": f"https://myapp.com/reset/{token}", }, ) return JsonResponse({"message": "Reset email sent"}) ``` ## 6. Send a weekly report (Management Command) Django Management Commands are the standard way to create scripts for cron jobs. ```python # myapp/management/commands/send_weekly_report.py from django.core.management.base import BaseCommand from myapp.services import sidemail class Command(BaseCommand): help = 'Sends weekly report email' def handle(self, *args, **options): # In a real app, fetch this from your database new_signups = 150 chart_data = [100, 200, 300, 400, 200, 300, 200, 500] sidemail.send_email( toAddress="admin@myapp.com", fromAddress="system@myapp.com", fromName="My App", templateName="Weekly Report", templateProps={ "signups": new_signups, "chart": chart_data, }, ) self.stdout.write(self.style.SUCCESS('Weekly report sent')) ``` Run it via cron: ```bash # Run every Monday at 8:00 AM 0 8 * * 1 python manage.py send_weekly_report ``` ## 7. Send email via Signals Decouple email sending from your views using Django Signals. ```python # myapp/signals.py from django.db.models.signals import post_save from django.dispatch import receiver from django.contrib.auth.models import User from .services import sidemail @receiver(post_save, sender=User) def send_welcome_email(sender, instance, created, **kwargs): if created: sidemail.send_email( toAddress=instance.email, fromAddress="welcome@myapp.com", fromName="My App", templateName="Welcome", ) # Don't forget to import signals in your apps.py ready() method! ``` ## 8. Send HTML email (with Django Templates) Use `render_to_string` to generate HTML from Django templates. ```python from django.template.loader import render_to_string def send_invoice(request): # templates/emails/invoice.html html_content = render_to_string('emails/invoice.html', {'amount': 99}) sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", subject="Your Invoice", html=html_content, ) return JsonResponse({"message": "Invoice sent"}) ``` ## 9. Send Markdown email Store your markdown content in a file and load it ([learn more](https://sidemail.io/docs/markdown-emails/)). ```python import os from django.conf import settings def send_markdown(request): # Assuming templates are in your templates directory template_path = os.path.join(settings.BASE_DIR, 'templates/emails/welcome.md') with open(template_path, "r") as f: markdown_content = f.read() # Subject and sender are defined in the markdown frontmatter sidemail.send_email( toAddress="user@email.com", markdown=markdown_content, templateProps={ "name": "John", "link": "https://example.com", }, ) return JsonResponse({"message": "Email sent"}) ``` ## 10. Send plain text email ```python sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", subject="Hello", text="Hello! 👋", ) ``` ## 11. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```python import datetime # Schedule for 1 hour from now scheduled_at = (datetime.datetime.utcnow() + datetime.timedelta(hours=1)).isoformat() + "Z" sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", templateName="Welcome", templateProps={"firstName": "Alex"}, scheduledAt=scheduled_at, ) ``` ## 12. Send with attachment Use the `Sidemail.file_to_attachment` helper to attach files. ```python from sidemail import Sidemail with open("invoice.pdf", "rb") as f: pdf_data = f.read() attachment = Sidemail.file_to_attachment("invoice.pdf", pdf_data) sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", subject="Your invoice", text="See attached.", attachments=[attachment], ) ``` ## 13. Handle errors ```python from sidemail import SidemailError import logging logger = logging.getLogger(__name__) try: sidemail.send_email( # ... ) except SidemailError as e: logger.error(f"Sidemail error: {e.message}") ``` --- # Send emails with Express.js Source: https://sidemail.io/docs/send-emails-with-expressjs/index.md # Send emails with Express.js In this quickstart, you'll learn how to send transactional emails from your [Express.js](https://expressjs.com/) app with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and scheduled reports. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash npm install sidemail ``` ## 2. Add your API key Add your Sidemail API key to `.env` (make sure you have `dotenv` installed): ```bash SIDEMAIL_API_KEY=your-api-key ``` ## 3. Setup Create a `sidemail.js` file that exports the configured instance. ```js // create file -> sidemail.js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: process.env.SIDEMAIL_API_KEY, }); module.exports = sidemail; ``` ## 4. Send a welcome email Send an email when a user registers. ```js // routes/auth.js const express = require("express"); const router = express.Router(); const sidemail = require("./sidemail"); router.post("/register", async (req, res, next) => { try { const { email, name } = req.body; // ... create user in database ... await sidemail.sendEmail({ toAddress: email, fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Welcome", templateProps: { firstName: name, }, }); res.status(201).json({ message: "User registered" }); } catch (error) { next(error); } }); module.exports = router; ``` ## 5. Send a password reset email ```js // routes/auth.js router.post("/forgot-password", async (req, res, next) => { try { const { email } = req.body; const token = "secure-reset-token"; // Generate this securely await sidemail.sendEmail({ toAddress: email, fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Password Reset", templateProps: { actionUrl: `https://myapp.com/reset/${token}`, }, }); res.json({ message: "Reset email sent" }); } catch (error) { next(error); } }); ``` ## 6. Send a weekly report (Cron Task) For scheduled tasks in an Express app, you can use a library like `node-cron`. ```js // jobs/weekly-report.js const cron = require("node-cron"); const sidemail = require("./sidemail"); // Run every Monday at 8:00 AM cron.schedule("0 8 * * 1", async () => { const newSignups = 150; // Fetch from DB const chartData = [100, 200, 300, 400, 200, 300, 200, 500]; try { await sidemail.sendEmail({ toAddress: "admin@myapp.com", fromAddress: "system@myapp.com", fromName: "My App", templateName: "Weekly Report", templateProps: { signups: newSignups, chart: chartData, }, }); console.log("Weekly report sent"); } catch (error) { console.error("Failed to send weekly report", error); } }); ``` ## 7. Send email via Event Emitter Decouple email sending from your routes by using Node.js `EventEmitter`. ```js // events.js const EventEmitter = require("events"); const sidemail = require("./sidemail"); class AppEmitter extends EventEmitter {} const appEmitter = new AppEmitter(); appEmitter.on("userRegistered", async (user) => { try { await sidemail.sendEmail({ toAddress: user.email, fromAddress: "welcome@myapp.com", fromName: "My App", templateName: "Welcome", }); } catch (error) { console.error("Failed to send welcome email", error); } }); module.exports = appEmitter; ``` Usage in route: ```js // routes/auth.js const appEmitter = require("../events"); router.post("/register", async (req, res, next) => { // ... create user ... const user = { email: "user@email.com", name: "Alex" }; // Emit the event appEmitter.emit("userRegistered", user); res.status(201).json({ message: "User registered" }); }); ``` ## 8. Send HTML email You can use template literals or a template engine like EJS or Pug to generate HTML. ```js router.post("/send-invoice", async (req, res, next) => { try { const amount = 99; const html = `

Invoice

Amount due: $${amount}

`; await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Your Invoice", html: html, }); res.json({ message: "Invoice sent" }); } catch (error) { next(error); } }); ``` ## 9. Send Markdown email Store your markdown content in a file and load it ([learn more](https://sidemail.io/docs/markdown-emails/)). ```js const fs = require("fs"); const path = require("path"); router.post("/send-markdown", async (req, res, next) => { try { const markdown = fs.readFileSync( path.join(__dirname, "../templates/emails/welcome.md"), "utf8" ); // Subject and sender are defined in the markdown frontmatter await sidemail.sendEmail({ toAddress: "user@email.com", markdown: markdown, templateProps: { name: "John", link: "https://example.com", }, }); res.json({ message: "Email sent" }); } catch (error) { next(error); } }); ``` ## 10. Send plain text email ```js await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Hello", text: "Hello! 👋", }); ``` ## 11. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```js const scheduledAt = new Date(Date.now() + 60 * 60 * 1000).toISOString(); await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Welcome", templateProps: { firstName: "Alex" }, scheduledAt: scheduledAt, }); ``` ## 12. Send with attachment Use the `sidemail.fileToAttachment` helper to attach files. ```js const fs = require("fs"); const pdfData = fs.readFileSync("./invoice.pdf"); const attachment = sidemail.fileToAttachment("invoice.pdf", pdfData); await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Your invoice", text: "See attached.", attachments: [attachment], }); ``` ## 13. Handle errors In Express, pass errors to the global error handler using `next(error)`. ```js try { await sidemail.sendEmail({ /* ... */ }); } catch (error) { next(error); } ``` --- # Send emails with FastAPI Source: https://sidemail.io/docs/send-emails-with-fastapi/index.md # Send emails with FastAPI In this quickstart, you'll learn how to send transactional emails from your [FastAPI](https://fastapi.tiangolo.com/) app with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and background tasks. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash pip install sidemail ``` ## 2. Configure API key Add your Sidemail API key to `.env`: ```bash SIDEMAIL_API_KEY=your-api-key ``` ## 3. Setup client Create a shared instance of the Sidemail client. ```python # services.py from sidemail import Sidemail import os # The SDK automatically reads SIDEMAIL_API_KEY from environment variables sidemail = Sidemail() ``` ## 4. Send a welcome email Send an email when a user registers. We use a standard `def` route so FastAPI runs the synchronous Sidemail SDK in a threadpool, preventing it from blocking the event loop. ```python # main.py from fastapi import FastAPI from pydantic import BaseModel from .services import sidemail app = FastAPI() class User(BaseModel): email: str name: str @app.post("/register") def register(user: User): # ... create user in database ... sidemail.send_email( toAddress=user.email, fromAddress="you@yourdomain.com", fromName="Your App", templateName="Welcome", templateProps={"firstName": user.name}, ) return {"message": "User registered"} ``` ## 5. Send a password reset email ```python # main.py class PasswordResetRequest(BaseModel): email: str @app.post("/forgot-password") def forgot_password(request: PasswordResetRequest): token = "secure-reset-token" # Generate this securely sidemail.send_email( toAddress=request.email, fromAddress="you@yourdomain.com", fromName="Your App", templateName="Password Reset", templateProps={ "actionUrl": f"https://myapp.com/reset/{token}", }, ) return {"message": "Reset email sent"} ``` ## 6. Send a weekly report You can create a standalone script for cron jobs. ```python # scripts/send_weekly_report.py from myapp.services import sidemail def main(): # In a real app, fetch this from your database new_signups = 150 chart_data = [100, 200, 300, 400, 200, 300, 200, 500] sidemail.send_email( toAddress="admin@myapp.com", fromAddress="system@myapp.com", fromName="My App", templateName="Weekly Report", templateProps={ "signups": new_signups, "chart": chart_data, }, ) print("Weekly report sent") if __name__ == "__main__": main() ``` Run it via cron: ```bash # Run every Monday at 8:00 AM 0 8 * * 1 python -m scripts.send_weekly_report ``` ## 7. Send email via Background Tasks Decouple email sending from the response using FastAPI's `BackgroundTasks`. This allows you to return a response immediately while the email sends in the background. ```python # main.py from fastapi import BackgroundTasks def send_welcome_email_task(email: str, name: str): sidemail.send_email( toAddress=email, fromAddress="welcome@myapp.com", fromName="My App", templateName="Welcome", templateProps={"firstName": name}, ) @app.post("/register-async") async def register_async(user: User, background_tasks: BackgroundTasks): # ... create user in database ... background_tasks.add_task(send_welcome_email_task, user.email, user.name) return {"message": "User registered, email sending in background"} ``` ## 8. Send HTML email Use `jinja2` to generate HTML from templates. ```python from fastapi.templating import Jinja2Templates templates = Jinja2Templates(directory="templates") @app.post("/send-invoice") def send_invoice(email: str): # templates/invoice.html template = templates.get_template("invoice.html") html_content = template.render(amount=99) sidemail.send_email( toAddress=email, fromAddress="you@yourdomain.com", fromName="Your App", subject="Your Invoice", html=html_content, ) return {"message": "Invoice sent"} ``` ## 9. Send Markdown email Store your markdown content in a file and load it ([learn more](https://sidemail.io/docs/markdown-emails/)). ```python def send_markdown(): with open("templates/emails/welcome.md", "r") as f: markdown_content = f.read() # Subject and sender are defined in the markdown frontmatter sidemail.send_email( toAddress="user@email.com", markdown=markdown_content, templateProps={ "name": "John", "link": "https://example.com", }, ) ``` ## 10. Send plain text email ```python sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", subject="Hello", text="Hello! 👋", ) ``` ## 11. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```python import datetime # Schedule for 1 hour from now scheduled_at = (datetime.datetime.utcnow() + datetime.timedelta(hours=1)).isoformat() + "Z" sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", templateName="Welcome", templateProps={"firstName": "Alex"}, scheduledAt=scheduled_at, ) ``` ## 12. Send with attachment Use the `Sidemail.file_to_attachment` helper to attach files. ```python from sidemail import Sidemail with open("invoice.pdf", "rb") as f: pdf_data = f.read() attachment = Sidemail.file_to_attachment("invoice.pdf", pdf_data) sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", subject="Your invoice", text="See attached.", attachments=[attachment], ) ``` ## 13. Handle errors ```python from sidemail import SidemailError import logging logger = logging.getLogger(__name__) try: sidemail.send_email( # ... ) except SidemailError as e: logger.error(f"Sidemail error: {e.message}") ``` --- # Send emails with Flask Source: https://sidemail.io/docs/send-emails-with-flask/index.md # Send emails with Flask In this quickstart, you'll learn how to send transactional emails from your Flask app with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and scheduled reports. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash pip install sidemail ``` ## 2. Add your API key Add your Sidemail API key to `.env` (make sure you have `python-dotenv` installed): ```bash SIDEMAIL_API_KEY=your-api-key ``` ## 3. Setup Create a shared instance of the Sidemail client. This allows you to import the configured client anywhere in your application (e.g., in Blueprints). ```python # sidemail_client.py from sidemail import Sidemail import os # The SDK automatically reads SIDEMAIL_API_KEY from environment variables sidemail = Sidemail() ``` ## 4. Send a welcome email Send an email when a user registers. ```python # app.py from flask import Flask, request, jsonify from sidemail_client import sidemail app = Flask(__name__) @app.route('/register', methods=['POST']) def register(): data = request.get_json() email = data.get('email') name = data.get('name') # ... create user in database ... sidemail.send_email( toAddress=email, fromAddress="you@yourdomain.com", fromName="Your App", templateName="Welcome", templateProps={"firstName": name}, ) return jsonify({"message": "User registered"}) ``` ## 5. Send a password reset email ```python @app.route('/forgot-password', methods=['POST']) def forgot_password(): email = request.get_json().get('email') token = "secure-reset-token" # Generate this securely sidemail.send_email( toAddress=email, fromAddress="you@yourdomain.com", fromName="Your App", templateName="Password Reset", templateProps={ "actionUrl": f"https://myapp.com/reset/{token}", }, ) return jsonify({"message": "Reset email sent"}) ``` ## 6. Send a weekly report (CLI Command) Flask has built-in support for creating CLI commands, which is perfect for cron jobs. ```python # app.py (or in a separate commands module) import click @app.cli.command("send-weekly-report") def send_weekly_report(): # In a real app, fetch this from your database new_signups = 150 chart_data = [100, 200, 300, 400, 200, 300, 200, 500] sidemail.send_email( toAddress="admin@myapp.com", fromAddress="system@myapp.com", fromName="My App", templateName="Weekly Report", templateProps={ "signups": new_signups, "chart": chart_data, }, ) click.echo("Weekly report sent") ``` Run it via cron: ```bash # Run every Monday at 8:00 AM 0 8 * * 1 flask send-weekly-report ``` ## 7. Send email via Signals Decouple email sending from your routes by using Flask signals (requires `blinker` library). ```python # signals.py from flask import signals user_registered = signals.Namespace().signal('user-registered') # app.py from signals import user_registered @user_registered.connect def send_welcome_email(sender, user): sidemail.send_email( toAddress=user['email'], fromAddress="welcome@myapp.com", fromName="My App", templateName="Welcome", ) @app.route('/register', methods=['POST']) def register(): # ... create user ... user = {"email": "user@email.com", "name": "Alex"} # Trigger the signal user_registered.send(app, user=user) return jsonify({"message": "User registered"}) ``` ## 8. Send HTML email (with Jinja2) Use Flask's `render_template` to generate HTML from your Jinja2 templates. ```python from flask import render_template @app.route('/send-invoice', methods=['POST']) def send_invoice(): # templates/emails/invoice.html html_content = render_template('emails/invoice.html', amount=99) sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", subject="Your Invoice", html=html_content, ) return jsonify({"message": "Invoice sent"}) ``` ## 9. Send Markdown email Store your markdown content in a file and load it ([learn more](https://sidemail.io/docs/markdown-emails/)). ```python import os @app.route('/send-markdown', methods=['POST']) def send_markdown(): # Assuming templates are in the 'templates' folder template_path = os.path.join(app.root_path, 'templates/emails/welcome.md') with open(template_path, "r") as f: markdown_content = f.read() # Subject and sender are defined in the markdown frontmatter sidemail.send_email( toAddress="user@email.com", markdown=markdown_content, templateProps={ "name": "John", "link": "https://example.com", }, ) return jsonify({"message": "Email sent"}) ``` ## 10. Send plain text email ```python sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", subject="Hello", text="Hello! 👋", ) ``` ## 11. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```python import datetime # Schedule for 1 hour from now scheduled_at = (datetime.datetime.utcnow() + datetime.timedelta(hours=1)).isoformat() + "Z" sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", templateName="Welcome", templateProps={"firstName": "Alex"}, scheduledAt=scheduled_at, ) ``` ## 12. Send with attachment Use the `Sidemail.file_to_attachment` helper to attach files. ```python from sidemail import Sidemail with open("invoice.pdf", "rb") as f: pdf_data = f.read() attachment = Sidemail.file_to_attachment("invoice.pdf", pdf_data) sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", subject="Your invoice", text="See attached.", attachments=[attachment], ) ``` ## 13. Handle errors ```python from sidemail import SidemailError try: sidemail.send_email( # ... ) except SidemailError as e: # Log error to file or monitoring system app.logger.error(f"Sidemail error: {e.message}") return jsonify({"error": "Failed to send email"}), 500 ``` --- # Send emails with Laravel Source: https://sidemail.io/docs/send-emails-with-laravel/index.md # Send emails with Laravel In this quickstart, you'll learn how to send transactional emails from your [Laravel](https://laravel.com/) 10 or 11 app with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and scheduled reports. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash composer require sidemail/sidemail ``` ## 2. Add your API key Add your Sidemail API key to `.env`: ```bash SIDEMAIL_API_KEY=your-api-key ``` ## 3. Configure the service Register the Sidemail client in `AppServiceProvider` to enable dependency injection. ```php // app/Providers/AppServiceProvider.php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Sidemail\Sidemail; class AppServiceProvider extends ServiceProvider { public function register(): void { $this->app->singleton(Sidemail::class, function () { return new Sidemail(); }); } } ``` ## 4. Send a welcome email with Laravel Inject `Sidemail\Sidemail` into your controller method. ```php namespace App\Http\Controllers; use Illuminate\Http\Request; use Sidemail\Sidemail; class RegistrationController extends Controller { public function store(Request $request, Sidemail $sidemail) { // ... create user ... $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Welcome', 'templateProps' => ['firstName' => 'Alex'], ]); return response('User registered!'); } } ``` ## 5. Send a password reset email with Laravel ```php // app/Http/Controllers/SecurityController.php namespace App\Http\Controllers; use Illuminate\Http\Request; use Sidemail\Sidemail; class SecurityController extends Controller { public function requestReset(Request $request, Sidemail $sidemail) { $validated = $request->validate(['email' => 'required|email']); $email = $validated['email']; // ... find user, generate token ... $sidemail->sendEmail([ 'toAddress' => $email, 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Password Reset', 'templateProps' => [ 'actionUrl' => 'https://myapp.com/reset/token123', ], ]); return response('Reset link sent!'); } } ``` ## 6. Send a weekly report (Artisan Command) with Laravel Create a command to send emails on a schedule. ```php // app/Console/Commands/SendWeeklyReport.php namespace App\Console\Commands; use Illuminate\Console\Command; use Sidemail\Sidemail; class SendWeeklyReport extends Command { protected $signature = 'app:send-weekly-report'; protected $description = 'Send weekly report email'; public function handle(Sidemail $sidemail) { // In a real app, you would calculate this from the database $newSignups = 150; $chartData = [100, 200, 300, 400, 200, 300, 200, 500]; $sidemail->sendEmail([ 'toAddress' => 'admin@myapp.com', 'fromAddress' => 'system@myapp.com', 'fromName' => 'My App', 'templateName' => 'Weekly Report', 'templateProps' => [ 'signups' => $newSignups, 'chart' => $chartData, ], ]); $this->info('Weekly report sent!'); } } ``` You can now run this command manually or schedule it in `routes/console.php`: ```php // routes/console.php use Illuminate\Support\Facades\Schedule; Schedule::command('app:send-weekly-report')->weeklyOn(1, '8:00'); ``` ## 7. Send email via Event Listener with Laravel Decouple email sending from your controllers by using an event listener. ```php // app/Listeners/SendWelcomeEmail.php namespace App\Listeners; use App\Events\UserRegistered; use Sidemail\Sidemail; class SendWelcomeEmail { public function __construct( private Sidemail $sidemail ) {} public function handle(UserRegistered $event): void { $this->sidemail->sendEmail([ 'toAddress' => $event->user->email, 'fromAddress' => 'welcome@myapp.com', 'fromName' => 'My App', 'templateName' => 'Welcome', ]); } } ``` ## 8. Send HTML email (with Blade) Use `view()->render()` to generate HTML from a Blade template. ```php // app/Http/Controllers/InvoiceController.php public function sendInvoice(Sidemail $sidemail) { $html = view('emails.invoice', [ 'amount' => 99, ])->render(); $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Your Invoice', 'html' => $html, ]); return response('Invoice sent!'); } ``` ## 9. Send Markdown email Store your markdown content in a file (e.g. `resources/views/emails/welcome.md`) and load it. ```php $markdown = file_get_contents(resource_path('views/emails/welcome.md')); $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Testing markdown emails 😊', 'markdown' => $markdown, 'templateProps' => [ 'name' => 'John', 'link' => 'https://example.com', ], ]); ``` ## 10. Send plain text email ```php $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Hello', 'text' => 'Hello! 👋', ]); ``` ## 11. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```php $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Welcome', 'templateProps' => ['firstName' => 'Alex'], 'scheduledAt' => now()->addHour()->toIso8601String(), ]); ``` ## 12. Send with attachment Use the `Sidemail::fileToAttachment` helper to attach files. ```php use Sidemail\Sidemail; $pdfData = file_get_contents(storage_path('app/invoices/invoice.pdf')); $attachment = Sidemail::fileToAttachment('invoice.pdf', $pdfData); $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Your invoice', 'text' => 'See attached.', 'attachments' => [$attachment], ]); ``` ## 13. Handle errors ```php use Sidemail\SidemailException; use Illuminate\Support\Facades\Log; public function send(Sidemail $sidemail) { try { $sidemail->sendEmail([/* ... */]); } catch (SidemailException $e) { Log::error('Sidemail error', [ 'message' => $e->getMessage(), 'httpStatus' => $e->getHttpStatus(), 'errorCode' => $e->getErrorCode(), ]); return response('Error sending email', 500); } return response('Email sent!'); } ``` --- # Send emails with Next.js Source: https://sidemail.io/docs/send-emails-with-nextjs/index.md # Send emails with Next.js In this quickstart, you'll learn how to send transactional emails from your [Next.js](https://nextjs.org/) 13, 14, or 15 (App Router) app with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and server actions. **Note:** The Sidemail SDK is designed for server-side usage only. Use it in Route Handlers, Server Actions, or Server Components. Do not use it in Client Components. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash npm install sidemail ``` ## 2. Configure API key Add your Sidemail API key to `.env.local`: ```bash SIDEMAIL_API_KEY=your-api-key ``` ## 3. Setup client Create a shared instance of the Sidemail client. ```javascript // lib/sidemail.js import configureSidemail from "sidemail"; const sidemail = configureSidemail({ apiKey: process.env.SIDEMAIL_API_KEY, }); export default sidemail; ``` ## 4. Send a welcome email Send an email when a user registers using a Route Handler. ```javascript // app/api/register/route.js import { NextResponse } from "next/server"; import sidemail from "@/lib/sidemail"; export async function POST(request) { const { email, name } = await request.json(); // ... create user in database ... await sidemail.sendEmail({ toAddress: email, fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Welcome", templateProps: { firstName: name }, }); return NextResponse.json({ message: "User registered" }); } ``` ## 5. Send a password reset email ```javascript // app/api/auth/forgot-password/route.js import { NextResponse } from "next/server"; import sidemail from "@/lib/sidemail"; export async function POST(request) { const { email } = await request.json(); const token = "secure-reset-token"; // Generate this securely await sidemail.sendEmail({ toAddress: email, fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Password Reset", templateProps: { actionUrl: `https://myapp.com/reset/${token}`, }, }); return NextResponse.json({ message: "Reset email sent" }); } ``` ## 6. Send a weekly report (Cron Job) Next.js doesn't have a built-in scheduler. Instead, you create an API route and trigger it using an external service like Vercel Cron, GitHub Actions, or a standard cron job. ```javascript // app/api/cron/weekly-report/route.js import { NextResponse } from "next/server"; import sidemail from "@/lib/sidemail"; export async function GET() { // In a real app, fetch this from your database const newSignups = 150; const chartData = [100, 200, 300, 400, 200, 300, 200, 500]; await sidemail.sendEmail({ toAddress: "admin@myapp.com", fromAddress: "system@myapp.com", fromName: "My App", templateName: "Weekly Report", templateProps: { signups: newSignups, chart: chartData, }, }); return NextResponse.json({ message: "Weekly report sent" }); } ``` ## 7. Send email via Server Action Send an email directly from a form submission using Server Actions. ```javascript // app/actions.js "use server"; import sidemail from "@/lib/sidemail"; export async function sendInvoice(formData) { const email = formData.get("email"); await sidemail.sendEmail({ toAddress: email, fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Your Invoice", text: "Here is your invoice.", }); } ``` ## 8. Send HTML email ```javascript // app/api/send-html/route.js import { NextResponse } from "next/server"; import sidemail from "@/lib/sidemail"; export async function POST(request) { const { email } = await request.json(); const htmlContent = `

Invoice

Amount due: $99

`; await sidemail.sendEmail({ toAddress: email, fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Your Invoice", html: htmlContent, }); return NextResponse.json({ message: "Invoice sent" }); } ``` ## 9. Send Markdown email Store your markdown content in a file and load it ([learn more](https://sidemail.io/docs/markdown-emails/)). ```javascript import { promises as fs } from "fs"; import path from "path"; import sidemail from "@/lib/sidemail"; export async function POST(request) { const templatePath = path.join(process.cwd(), "templates/emails/welcome.md"); const markdownContent = await fs.readFile(templatePath, "utf8"); await sidemail.sendEmail({ toAddress: "user@email.com", markdown: markdownContent, templateProps: { name: "John", link: "https://example.com", }, }); // ... } ``` ## 10. Send plain text email ```javascript await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Hello", text: "Hello! 👋", }); ``` ## 11. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```javascript // Schedule for 1 hour from now const scheduledAt = new Date(Date.now() + 60 * 60 * 1000).toISOString(); await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Welcome", templateProps: { firstName: "Alex" }, scheduledAt: scheduledAt, }); ``` ## 12. Send with attachment Use the `sidemail.fileToAttachment` helper to attach files. ```javascript import { promises as fs } from "fs"; import path from "path"; import sidemail from "@/lib/sidemail"; const pdfPath = path.join(process.cwd(), "public", "invoice.pdf"); const pdfData = await fs.readFile(pdfPath); const attachment = sidemail.fileToAttachment("invoice.pdf", pdfData); await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Your invoice", text: "See attached.", attachments: [attachment], }); ``` ## 13. Handle errors ```javascript try { await sidemail.sendEmail({ // ... }); } catch (error) { console.error("Sidemail error:", error.message); return NextResponse.json({ error: "Failed to send email" }, { status: 500 }); } ``` --- # Send emails with Node.js Source: https://sidemail.io/docs/send-emails-with-nodejs/index.md # Send emails with Node.js In this quickstart, you'll learn how to send transactional emails from your Node.js project with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and scheduled reports. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash npm install sidemail ``` ## 2. Setup Initialize the client. The SDK automatically reads `SIDEMAIL_API_KEY` from your environment variables if you don't provide it explicitly, but passing it is recommended for clarity. ### Option A: Simple setup Use this for simple scripts. ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: process.env.SIDEMAIL_API_KEY, }); ``` ### Option B: Shared instance (Module) For larger applications, create a `sidemail.js` file that exports the configured instance. ```js // create file -> sidemail.js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: process.env.SIDEMAIL_API_KEY, }); module.exports = sidemail; ``` Usage: ```js const sidemail = require("./sidemail"); await sidemail.sendEmail({ /* ... */ }); ``` ## 3. Send a welcome email ```js await sidemail.sendEmail({ toAddress: user.email, fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Welcome", templateProps: { firstName: user.name, }, }); ``` ## 4. Send a password reset email ```js const { email } = req.body; await sidemail.sendEmail({ toAddress: email, fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Password Reset", templateProps: { actionUrl: "https://myapp.com/reset/token123", }, }); ``` ## 5. Send a weekly report (Cron Task) Create a standalone script to run via cron or use a library like `node-cron`. ```js // scripts/send-weekly-report.js const sidemail = require("../sidemail"); async function init() { // In a real app, fetch this from your database const newSignups = 150; const chartData = [100, 200, 300, 400, 200, 300, 200, 500]; await sidemail.sendEmail({ toAddress: "admin@myapp.com", fromAddress: "system@myapp.com", fromName: "My App", templateName: "Weekly Report", templateProps: { signups: newSignups, chart: chartData, }, }); } init(); ``` Run it via cron: ```bash # Run every Monday at 8:00 AM 0 8 * * 1 node /path/to/scripts/send-weekly-report.js ``` ## 6. Send HTML email You can use template literals to render HTML directly in your code. ```js const amount = 99; const html = `

Invoice

Amount due: $${amount}

`; await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Your Invoice", html: html, }); ``` ## 7. Send Markdown email Store your markdown content in a file and load it ([learn more](https://sidemail.io/docs/markdown-emails/)). ```js const fs = require("fs"); const path = require("path"); const markdown = fs.readFileSync( path.join(__dirname, "templates/emails/welcome.md"), "utf8" ); // Subject and sender are defined in the markdown frontmatter await sidemail.sendEmail({ toAddress: "user@email.com", markdown: markdown, templateProps: { name: "John", link: "https://example.com", }, }); ``` ## 8. Send plain text email ```js await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Hello", text: "Hello! 👋", }); ``` ## 9. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```js // Schedule for 1 hour from now const scheduledAt = new Date(Date.now() + 60 * 60 * 1000).toISOString(); await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Welcome", templateProps: { firstName: "Alex" }, scheduledAt: scheduledAt, }); ``` ## 10. Send with attachment Use the `sidemail.fileToAttachment` helper to attach files. ```js const fs = require("fs"); const pdfData = fs.readFileSync("./invoice.pdf"); const attachment = sidemail.fileToAttachment("invoice.pdf", pdfData); await sidemail.sendEmail({ toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Your invoice", text: "See attached.", attachments: [attachment], }); ``` ## 11. Handle errors ```js try { await sidemail.sendEmail({ /* ... */ }); } catch (error) { // Log error to file or monitoring system console.error("Sidemail error:", error.message); } ``` --- # Send emails with PHP Source: https://sidemail.io/docs/send-emails-with-php/index.md # Send emails with PHP In this quickstart, you'll learn how to send transactional emails from your PHP project with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and scheduled reports. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash composer require sidemail/sidemail ``` ## 2. Setup Initialize the client. The SDK automatically reads `SIDEMAIL_API_KEY` from your environment variables. ### Option A: Simple setup Use this for simple scripts. ```php require_once __DIR__ . '/vendor/autoload.php'; use Sidemail\Sidemail; $sidemail = new Sidemail(); // or manual: new Sidemail(apiKey: '...'); ``` ### Option B: Shared instance (Singleton) For larger applications, create a `sidemail.php` file that returns a singleton instance. ```php // create file -> sidemail.php require_once __DIR__ . '/vendor/autoload.php'; use Sidemail\Sidemail; // Define a helper function to hold the static instance if (!function_exists('sidemail_instance')) { function sidemail_instance() { static $instance; if (!$instance) { $instance = new Sidemail(); } return $instance; } } return sidemail_instance(); ``` Usage: ```php $sidemail = require __DIR__ . '/sidemail.php'; $sidemail->sendEmail([/* ... */]); ``` ## 3. Send a welcome email ```php $sidemail->sendEmail([ 'toAddress' => $email, 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Welcome', 'templateProps' => ['firstName' => $name], ]); ``` ## 4. Send a password reset email Handle form submissions in your script. ```php $email = $_POST['email']; $sidemail->sendEmail([ 'toAddress' => $email, 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Password Reset', 'templateProps' => [ 'actionUrl' => 'https://myapp.com/reset/token123', ], ]); ``` ## 5. Send a weekly report (Cron Task) Create a standalone script to run via cron. ```php // scripts/send-weekly-report.php // In a real app, fetch this from your database $newSignups = 150; $chartData = [100, 200, 300, 400, 200, 300, 200, 500]; $sidemail->sendEmail([ 'toAddress' => 'admin@myapp.com', 'fromAddress' => 'system@myapp.com', 'fromName' => 'My App', 'templateName' => 'Weekly Report', 'templateProps' => [ 'signups' => $newSignups, 'chart' => $chartData, ], ]); ``` Run it via cron: ```bash # Run every Monday at 8:00 AM 0 8 * * 1 php /path/to/scripts/send-weekly-report.php ``` ## 6. Send HTML email (with PHP template) Use output buffering (`ob_start`) to render HTML from a PHP file. ```php // templates/invoice.php ob_start(); $amount = 99; include __DIR__ . '/templates/invoice.php'; $html = ob_get_clean(); $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Your Invoice', 'html' => $html, ]); ``` ## 7. Send Markdown email Store your markdown content in a file and load it ([learn more](https://sidemail.io/docs/markdown-emails/)). ```php $markdown = file_get_contents(__DIR__ . '/templates/emails/welcome.md'); // Subject and sender are defined in the markdown frontmatter $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'markdown' => $markdown, 'templateProps' => [ 'name' => 'John', 'link' => 'https://example.com', ], ]); ``` ## 8. Send plain text email ```php $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Hello', 'text' => 'Hello! 👋', ]); ``` ## 9. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```php $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Welcome', 'templateProps' => ['firstName' => 'Alex'], 'scheduledAt' => (new DateTime('+1 hour'))->format(DateTime::ATOM), ]); ``` ## 10. Send with attachment Use the `Sidemail::fileToAttachment` helper to attach files. ```php $pdfData = file_get_contents(__DIR__ . '/invoice.pdf'); $attachment = Sidemail::fileToAttachment('invoice.pdf', $pdfData); $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Your invoice', 'text' => 'See attached.', 'attachments' => [$attachment], ]); ``` ## 11. Handle errors ```php use Sidemail\SidemailException; try { $sidemail->sendEmail([/* ... */]); } catch (SidemailException $e) { // Log error to file or monitoring system error_log('Sidemail error: ' . $e->getMessage()); } ``` --- # Send emails with Python Source: https://sidemail.io/docs/send-emails-with-python/index.md # Send emails with Python In this quickstart, you'll learn how to send transactional emails from your Python project with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and scheduled reports. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash pip install sidemail ``` ## 2. Setup Initialize the client. The SDK automatically reads `SIDEMAIL_API_KEY` from your environment variables if you don't provide it explicitly. ### Option A: Simple setup Use this for simple scripts. ```python from sidemail import Sidemail import os # Reads SIDEMAIL_API_KEY from environment variables automatically # or pass api_key="your-key" sidemail = Sidemail() ``` ### Option B: Shared instance (Module) For larger applications, create a `sidemail_client.py` file that exports the configured instance. ```python # create file -> sidemail_client.py from sidemail import Sidemail import os sidemail = Sidemail(api_key=os.getenv("SIDEMAIL_API_KEY")) ``` Usage: ```python from sidemail_client import sidemail sidemail.send_email( # ... ) ``` ## 3. Send a welcome email ```python sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", templateName="Welcome", templateProps={"firstName": "Alex"}, ) ``` ## 4. Send a password reset email ```python sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", templateName="Password Reset", templateProps={ "actionUrl": "https://myapp.com/reset/token123", }, ) ``` ## 5. Send a weekly report (Cron Task) Create a standalone script to run via cron. ```python # scripts/send_weekly_report.py from sidemail_client import sidemail def main(): # In a real app, fetch this from your database new_signups = 150 chart_data = [100, 200, 300, 400, 200, 300, 200, 500] sidemail.send_email( toAddress="admin@myapp.com", fromAddress="system@myapp.com", fromName="My App", templateName="Weekly Report", templateProps={ "signups": new_signups, "chart": chart_data, }, ) if __name__ == "__main__": main() ``` Run it via cron: ```bash # Run every Monday at 8:00 AM 0 8 * * 1 python3 /path/to/scripts/send_weekly_report.py ``` ## 6. Send HTML email You can use f-strings to render HTML directly in your code. ```python amount = 99 html_content = f"""

Invoice

Amount due: ${amount}

""" sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", subject="Your Invoice", html=html_content, ) ``` ## 7. Send Markdown email Store your markdown content in a file and load it ([learn more](https://sidemail.io/docs/markdown-emails/)). ```python with open("templates/emails/welcome.md", "r") as f: markdown_content = f.read() # Subject and sender are defined in the markdown frontmatter sidemail.send_email( toAddress="user@email.com", markdown=markdown_content, templateProps={ "name": "John", "link": "https://example.com", }, ) ``` ## 8. Send plain text email ```python sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", subject="Hello", text="Hello! 👋", ) ``` ## 9. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```python import datetime # Schedule for 1 hour from now scheduled_at = (datetime.datetime.utcnow() + datetime.timedelta(hours=1)).isoformat() + "Z" sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", templateName="Welcome", templateProps={"firstName": "Alex"}, scheduledAt=scheduled_at, ) ``` ## 10. Send with attachment Use the `Sidemail.file_to_attachment` helper to attach files. ```python from sidemail import Sidemail with open("invoice.pdf", "rb") as f: pdf_data = f.read() attachment = Sidemail.file_to_attachment("invoice.pdf", pdf_data) sidemail.send_email( toAddress="user@email.com", fromAddress="you@yourdomain.com", fromName="Your App", subject="Your invoice", text="See attached.", attachments=[attachment], ) ``` ## 11. Handle errors ```python from sidemail import SidemailError try: sidemail.send_email( # ... ) except SidemailError as e: # Log error to file or monitoring system print(f"Sidemail error: {e.message}") ``` --- # Send emails with Ruby on Rails Source: https://sidemail.io/docs/send-emails-with-ruby-on-rails/index.md # Send emails with Ruby on Rails In this quickstart, you'll learn how to send transactional emails from your [Ruby on Rails](https://rubyonrails.org/) 6, 7, or 8 app with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and scheduled reports. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install Add the gem to your Gemfile: ```bash bundle add sidemail ``` ## 2. Add your API key Add your Sidemail API key to `.env` (if using `dotenv-rails`) or your credentials file. ```bash SIDEMAIL_API_KEY=your-api-key ``` ## 3. Configure the service We recommend creating a Service Object to manage the Sidemail client instance. This keeps your code clean and makes it easy to access the client throughout your application. ```ruby # app/services/sidemail_service.rb require "sidemail" class SidemailService def self.client # The SDK automatically reads SIDEMAIL_API_KEY from ENV @client ||= Sidemail.new end end ``` ## 4. Send a welcome email Send an email from your controller. ```ruby # app/controllers/registrations_controller.rb class RegistrationsController < ApplicationController def create @user = User.create(user_params) if @user.persisted? SidemailService.client.send_email( toAddress: @user.email, fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Welcome", templateProps: { firstName: @user.first_name } ) redirect_to root_path, notice: "User registered!" else render :new end end end ``` ## 5. Send a password reset email ```ruby # app/controllers/passwords_controller.rb class PasswordsController < ApplicationController def create user = User.find_by(email: params[:email]) if user token = user.generate_reset_token SidemailService.client.send_email( toAddress: user.email, fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Password Reset", templateProps: { actionUrl: edit_password_url(token: token) } ) end redirect_to login_path, notice: "If an account exists, we sent a reset link." end end ``` ## 6. Send a weekly report (Rake Task) Create a Rake task to send emails on a schedule. ```ruby # lib/tasks/reports.rake namespace :reports do desc "Send weekly report" task weekly: :environment do new_signups = User.where("created_at > ?", 1.week.ago).count chart_data = [100, 200, 300, 400, 200, 300, 200, 500] SidemailService.client.send_email( toAddress: "admin@myapp.com", fromAddress: "system@myapp.com", fromName: "My App", templateName: "Weekly Report", templateProps: { signups: new_signups, chart: chart_data } ) puts "Weekly report sent!" end end ``` Run it via cron or Heroku Scheduler: ```bash rake reports:weekly ``` ## 7. Send email via ActiveJob Decouple email sending from your controllers by using ActiveJob. ```ruby # app/jobs/send_welcome_email_job.rb class SendWelcomeEmailJob < ApplicationJob queue_as :default def perform(user_id) user = User.find(user_id) SidemailService.client.send_email( toAddress: user.email, fromAddress: "welcome@myapp.com", fromName: "My App", templateName: "Welcome", templateProps: { firstName: user.first_name } ) end end ``` Usage in controller: ```ruby SendWelcomeEmailJob.perform_later(@user.id) ``` ## 8. Send HTML email (with ERB) Use `ActionController::Base.render` to generate HTML from an ERB template. ```ruby # app/controllers/invoices_controller.rb def send_invoice html_content = ActionController::Base.new.render_to_string( template: "emails/invoice", layout: false, locals: { amount: 99 } ) SidemailService.client.send_email( toAddress: current_user.email, fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Your Invoice", html: html_content ) render json: { message: "Invoice sent!" } end ``` ## 9. Send Markdown email Store your markdown content in a file (e.g. `app/views/emails/welcome.md`) and load it. ```ruby markdown = File.read(Rails.root.join("app/views/emails/welcome.md")) # Subject and sender are defined in the markdown frontmatter SidemailService.client.send_email( toAddress: "user@email.com", markdown: markdown, templateProps: { name: "John", link: "https://example.com" } ) ``` ## 10. Send plain text email ```ruby SidemailService.client.send_email( toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Hello", text: "Hello! 👋" ) ``` ## 11. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```ruby SidemailService.client.send_email( toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Welcome", templateProps: { firstName: "Alex" }, scheduledAt: 1.hour.from_now.utc.iso8601 ) ``` ## 12. Send with attachment Use the `Sidemail.file_to_attachment` helper to attach files. ```ruby pdf_data = File.read(Rails.root.join("storage/invoices/invoice.pdf")) attachment = Sidemail.file_to_attachment("invoice.pdf", pdf_data) SidemailService.client.send_email( toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Your invoice", text: "See attached.", attachments: [attachment] ) ``` ## 13. Handle errors ```ruby begin SidemailService.client.send_email( # ... ) rescue Sidemail::Error => e Rails.logger.error("Sidemail error: #{e.message}") # Handle error (e.g. notify Sentry/Honeybadger) end ``` --- # Send emails with Ruby Source: https://sidemail.io/docs/send-emails-with-ruby/index.md # Send emails with Ruby In this quickstart, you'll learn how to send transactional emails from your Ruby app with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and scheduled reports. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash gem install sidemail ``` ## 2. Add your API key Add your Sidemail API key to your environment variables (e.g. in `.env` if you use `dotenv`): ```bash SIDEMAIL_API_KEY=your-api-key ``` ## 3. Setup Initialize the client. The SDK automatically reads `SIDEMAIL_API_KEY` from your environment variables if you don't provide it explicitly. ### Option A: Simple setup Use this for simple scripts. ```ruby require "sidemail" # Reads SIDEMAIL_API_KEY from environment variables automatically # or pass api_key: "your-key" sm = Sidemail.new ``` ### Option B: Shared instance For larger applications, create a `sidemail_client.rb` file that exports the configured instance. ```ruby # sidemail_client.rb require "sidemail" $sidemail = Sidemail.new ``` ## 4. Send a welcome email ```ruby require "sidemail" sm = Sidemail.new sm.send_email( toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Welcome", templateProps: { firstName: "Alex" } ) ``` ## 5. Send a password reset email ```ruby sm.send_email( toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Password Reset", templateProps: { actionUrl: "https://myapp.com/reset/token123" } ) ``` ## 6. Send a weekly report (Script) Create a standalone script to run via cron. ```ruby # scripts/send_weekly_report.rb require "sidemail" sm = Sidemail.new # In a real app, fetch this from your database new_signups = 150 chart_data = [100, 200, 300, 400, 200, 300, 200, 500] sm.send_email( toAddress: "admin@myapp.com", fromAddress: "system@myapp.com", fromName: "My App", templateName: "Weekly Report", templateProps: { signups: new_signups, chart: chart_data } ) puts "Weekly report sent" ``` ## 7. Send HTML email ```ruby html_content = "

Hello world! 👋

" sm.send_email( toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Testing HTML email", html: html_content ) ``` ## 8. Send Markdown email Store your markdown content in a file and load it ([learn more](https://sidemail.io/docs/markdown-emails/)). ```ruby markdown_content = File.read("templates/welcome.md") # Subject and sender are defined in the markdown frontmatter sm.send_email( toAddress: "user@email.com", markdown: markdown_content, templateProps: { name: "John", link: "https://example.com" } ) ``` ## 9. Send plain text email ```ruby sm.send_email( toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Hello", text: "Hello! 👋" ) ``` ## 10. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```ruby require "date" # Schedule for 1 hour from now scheduled_at = (Time.now + 60 * 60).utc.iso8601 sm.send_email( toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", templateName: "Welcome", templateProps: { firstName: "Alex" }, scheduledAt: scheduled_at ) ``` ## 11. Send with attachment Use the `Sidemail.file_to_attachment` helper to attach files. ```ruby file_content = File.read("invoice.pdf") attachment = Sidemail.file_to_attachment("invoice.pdf", file_content) sm.send_email( toAddress: "user@email.com", fromAddress: "you@yourdomain.com", fromName: "Your App", subject: "Your invoice", text: "See attached.", attachments: [attachment] ) ``` ## 12. Handle errors ```ruby begin sm.send_email( # ... ) rescue Sidemail::Error => e puts "Sidemail error: #{e.message}" puts "Status: #{e.http_status}" if e.http_status puts "Code: #{e.error_code}" if e.error_code end ``` --- # Send emails with Slim Source: https://sidemail.io/docs/send-emails-with-slim/index.md # Send emails with Slim In this quickstart, you'll learn how to send transactional emails from your [Slim Framework](https://www.slimframework.com/) 4 app with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and scheduled reports. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash composer require sidemail/sidemail ``` ## 2. Configure API key Add your Sidemail API key to `.env`: ```bash SIDEMAIL_API_KEY=your-api-key ``` ## 3. Configure the container Register the Sidemail client in your DI container (e.g., PHP-DI). ```php // app/dependencies.php use Sidemail\Sidemail; use Psr\Container\ContainerInterface; return function (ContainerInterface $container) { $container->set(Sidemail::class, function () { // The SDK automatically reads SIDEMAIL_API_KEY from env return new Sidemail(); }); }; ``` ## 4. Send a welcome email Inject `Sidemail\Sidemail` into your route handler. ```php // app/routes.php use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Sidemail\Sidemail; $app->post('/register', function (Request $request, Response $response) { $data = $request->getParsedBody(); // ... create user ... // Retrieve Sidemail from the container $sidemail = $this->get(Sidemail::class); $sidemail->sendEmail([ 'toAddress' => $data['email'], 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Welcome', 'templateProps' => ['firstName' => $data['name']], ]); $response->getBody()->write('User registered'); return $response; }); ``` ## 5. Send a password reset email ```php $app->post('/forgot-password', function (Request $request, Response $response) { $data = $request->getParsedBody(); $token = 'secure-reset-token'; $sidemail = $this->get(Sidemail::class); $sidemail->sendEmail([ 'toAddress' => $data['email'], 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Password Reset', 'templateProps' => [ 'actionUrl' => 'https://myapp.com/reset/' . $token, ], ]); $response->getBody()->write('Reset link sent'); return $response; }); ``` ## 6. Send a weekly report (Script) Create a standalone script for cron jobs. ```php // scripts/send-weekly-report.php require __DIR__ . '/../vendor/autoload.php'; use Sidemail\Sidemail; // Load .env if needed $dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../'); $dotenv->load(); $sidemail = new Sidemail(); $newSignups = 150; $chartData = [100, 200, 300, 400, 200, 300, 200, 500]; $sidemail->sendEmail([ 'toAddress' => 'admin@myapp.com', 'fromAddress' => 'system@myapp.com', 'fromName' => 'My App', 'templateName' => 'Weekly Report', 'templateProps' => [ 'signups' => $newSignups, 'chart' => $chartData, ], ]); echo "Weekly report sent\n"; ``` Run it via cron: ```bash # Run every Monday at 8:00 AM 0 8 * * 1 php scripts/send-weekly-report.php ``` ## 7. Send email via Middleware Decouple email sending using Middleware. This is useful for sending emails after a successful request without cluttering your route logic. ```php // src/Middleware/SendWelcomeEmail.php use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Server\RequestHandlerInterface as RequestHandler; use Slim\Psr7\Response; use Sidemail\Sidemail; class SendWelcomeEmail { private $sidemail; public function __construct(Sidemail $sidemail) { $this->sidemail = $sidemail; } public function __invoke(Request $request, RequestHandler $handler): Response { $response = $handler->handle($request); // Only send if registration was successful (e.g. 200 OK) if ($response->getStatusCode() === 200) { $data = $request->getParsedBody(); $this->sidemail->sendEmail([ 'toAddress' => $data['email'], 'fromAddress' => 'welcome@myapp.com', 'fromName' => 'My App', 'templateName' => 'Welcome', ]); } return $response; } } // Register middleware to the route $app->post('/register', \App\Action\RegisterAction::class) ->add(SendWelcomeEmail::class); ``` ## 8. Send HTML email ```php $app->post('/send-invoice', function (Request $request, Response $response) { $sidemail = $this->get(Sidemail::class); $html = '

Invoice

Amount due: $99

'; $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Your Invoice', 'html' => $html, ]); $response->getBody()->write('Invoice sent'); return $response; }); ``` ## 9. Send Markdown email Store your markdown content in a file and load it ([learn more](https://sidemail.io/docs/markdown-emails/)). ```php $markdown = file_get_contents(__DIR__ . '/../templates/emails/welcome.md'); $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Testing markdown emails 😊', 'markdown' => $markdown, 'templateProps' => [ 'name' => 'John', 'link' => 'https://example.com', ], ]); ``` ## 10. Send plain text email ```php $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Hello', 'text' => 'Hello! 👋', ]); ``` ## 11. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```php $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Welcome', 'templateProps' => ['firstName' => 'Alex'], 'scheduledAt' => (new DateTime('+1 hour'))->format(DateTime::ATOM), ]); ``` ## 12. Send with attachment Use the `Sidemail::fileToAttachment` helper to attach files. ```php use Sidemail\Sidemail; $pdfData = file_get_contents('invoice.pdf'); $attachment = Sidemail::fileToAttachment('invoice.pdf', $pdfData); $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Your invoice', 'text' => 'See attached.', 'attachments' => [$attachment], ]); ``` ## 13. Handle errors ```php use Sidemail\SidemailException; try { $sidemail->sendEmail([/* ... */]); } catch (SidemailException $e) { // Log error error_log($e->getMessage()); $response->withStatus(500)->getBody()->write('Error sending email'); return $response; } ``` --- # Send emails with Symfony Source: https://sidemail.io/docs/send-emails-with-symfony/index.md # Send emails with Symfony In this quickstart, you'll learn how to send transactional emails from your [Symfony](https://symfony.com/) 6 or 7 app with Sidemail. You'll set up the SDK, send your first email, and see examples for common use cases like welcome emails, password resets, and scheduled reports. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Install ```bash composer require sidemail/sidemail ``` ## 2. Add your API key Add your Sidemail API key to `.env`: ```bash SIDEMAIL_API_KEY=your-api-key ``` ## 3. Configure the service Register the Sidemail client as a service in `config/services.yaml`. The SDK reads `SIDEMAIL_API_KEY` automatically from env. ```yaml # config/services.yaml services: # ... other services Sidemail\Sidemail: autowire: true ``` ## 4. Send a welcome email with Symfony Inject `Sidemail\Sidemail` into your controller (e.g. `src/Controller/RegistrationController.php`). ```php namespace App\Controller; use Sidemail\Sidemail; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; class RegistrationController extends AbstractController { #[Route('/register', methods: ['POST'])] public function register(Sidemail $sidemail): Response { // ... create user ... $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Welcome', 'templateProps' => ['firstName' => 'Alex'], ]); return new Response('User registered!'); } } ``` ## 5. Send a password reset email with Symfony ```php // src/Controller/SecurityController.php use Sidemail\Sidemail; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; class SecurityController extends AbstractController { #[Route('/reset-password', methods: ['POST'])] public function requestReset(Request $request, Sidemail $sidemail): Response { // In a real app, use Symfony Forms for validation, find user, generate token ... $email = $request->request->get('email'); $token = 'secure-password-reset-token'; $sidemail->sendEmail([ 'toAddress' => $email, 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Password Reset', 'templateProps' => [ 'actionUrl' => 'https://myapp.com/reset/' . $token, ], ]); return new Response('Reset link sent!'); } } ``` ## 6. Send a weekly report (Cron Task) with Symfony Create a console command to send emails on a schedule. ```php // src/Command/SendWeeklyReportCommand.php namespace App\Command; use Sidemail\Sidemail; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; #[AsCommand(name: 'app:send-weekly-report')] class SendWeeklyReportCommand extends Command { public function __construct( private Sidemail $sidemail ) { parent::__construct(); } protected function execute(InputInterface $input, OutputInterface $output): int { // In a real app, you would calculate this from the database $newSignups = 150; $chartData = [100, 200, 300, 400, 200, 300, 200, 500]; $this->sidemail->sendEmail([ 'toAddress' => 'admin@myapp.com', 'fromAddress' => 'system@myapp.com', 'fromName' => 'My App', 'templateName' => 'Weekly Report', 'templateProps' => [ 'signups' => $newSignups, 'chart' => $chartData, ], ]); return Command::SUCCESS; } } ``` You can now run this command manually or schedule it with cron: ```bash # Run every Monday at 8:00 AM 0 8 * * 1 bin/console app:send-weekly-report ``` ## 7. Send email via Event Subscriber with Symfony Decouple email sending from your controllers by using an event subscriber. ```php // src/EventSubscriber/UserRegisteredSubscriber.php namespace App\EventSubscriber; use App\Event\UserRegisteredEvent; use Sidemail\Sidemail; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class UserRegisteredSubscriber implements EventSubscriberInterface { public function __construct( private Sidemail $sidemail ) {} public static function getSubscribedEvents(): array { return [ UserRegisteredEvent::class => 'onUserRegistered', ]; } public function onUserRegistered(UserRegisteredEvent $event): void { $this->sidemail->sendEmail([ 'toAddress' => $event->getUser()->getEmail(), 'fromAddress' => 'welcome@myapp.com', 'fromName' => 'My App', 'templateName' => 'Welcome', ]); } } ``` ## 8. Send HTML email (with Twig) Use `$this->renderView()` to generate HTML from a Twig template. ```php // src/Controller/InvoiceController.php public function sendInvoice(Sidemail $sidemail): Response { $html = $this->renderView('emails/invoice.html.twig', [ 'amount' => 99, ]); $sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Your Invoice', 'html' => $html, ]); return new Response('Invoice sent!'); } ``` ## 9. Send Markdown email Store your markdown content in a file (e.g. `templates/emails/welcome.md`) and load it ([learn more](https://sidemail.io/docs/markdown-emails/)). ```php $markdown = file_get_contents($this->getParameter('kernel.project_dir') . '/templates/emails/welcome.md'); $this->sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Testing markdown emails 😊', 'markdown' => $markdown, 'templateProps' => [ 'name' => 'John', 'link' => 'https://example.com', ], ]); ``` ## 10. Send plain text email ```php $this->sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Hello', 'text' => 'Hello! 👋', ]); ``` ## 11. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```php $this->sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'templateName' => 'Welcome', 'templateProps' => ['firstName' => 'Alex'], 'scheduledAt' => (new \DateTimeImmutable('+1 hour'))->format(\DateTimeInterface::ATOM), ]); ``` ## 12. Send with attachment Use the `Sidemail::fileToAttachment` helper to attach files. ```php use Sidemail\Sidemail; $pdfData = file_get_contents($this->getParameter('kernel.project_dir') . '/var/invoice.pdf'); $attachment = Sidemail::fileToAttachment('invoice.pdf', $pdfData); $this->sidemail->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your App', 'subject' => 'Your invoice', 'text' => 'See attached.', 'attachments' => [$attachment], ]); ``` ## 13. Handle errors ```php use Sidemail\SidemailException; use Psr\Log\LoggerInterface; class EmailController extends AbstractController { public function send(Sidemail $sidemail, LoggerInterface $logger): Response { try { $sidemail->sendEmail([/* ... */]); } catch (SidemailException $e) { $logger->error('Sidemail error', [ 'message' => $e->getMessage(), 'httpStatus' => $e->getHttpStatus(), 'errorCode' => $e->getErrorCode(), ]); return new Response('Error sending email', 500); } return new Response('Email sent!'); } } ``` --- # Send emails with WordPress Source: https://sidemail.io/docs/send-emails-with-wordpress/index.md # Send emails with WordPress In this quickstart, you'll learn how to send transactional emails from your WordPress site using the Sidemail API. We'll use the native `wp_remote_post` function, so you don't need to install any external libraries or Composer packages. ## Before you start 1. [Create a Sidemail account](https://sidemail.io) → get your API key 2. [Add a sending domain](/docs/sending-identities/) → set up your domain for sending ## 1. Add your API key Add your API key to your `wp-config.php` file to keep it secure and accessible. ```php // wp-config.php define( 'SIDEMAIL_API_KEY', 'your-api-key' ); ``` ## 2. Create a helper function Add this function to your theme's `functions.php` or a custom plugin. This wrapper handles authentication and error checking. ```php // functions.php function sidemail_send_email( $args ) { $api_key = defined( 'SIDEMAIL_API_KEY' ) ? SIDEMAIL_API_KEY : ''; if ( empty( $api_key ) ) { return new WP_Error( 'missing_key', 'Sidemail API key is not defined.' ); } $response = wp_remote_post( 'https://api.sidemail.io/v1/email/send', array( 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Content-Type' => 'application/json', ), 'body' => json_encode( $args ), 'timeout' => 15, ) ); if ( is_wp_error( $response ) ) { return $response; } $body = wp_remote_retrieve_body( $response ); $code = wp_remote_retrieve_response_code( $response ); if ( $code >= 400 ) { return new WP_Error( 'api_error', 'Sidemail API Error: ' . $body ); } return json_decode( $body ); } ``` ## 3. Send a welcome email Hook into the `user_register` action to send an email when a new user signs up. ```php add_action( 'user_register', 'my_send_welcome_email', 10, 1 ); function my_send_welcome_email( $user_id ) { $user = get_userdata( $user_id ); $result = sidemail_send_email( array( 'toAddress' => $user->user_email, 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your Site', 'templateName' => 'Welcome', 'templateProps' => array( 'username' => $user->user_login, ), ) ); if ( is_wp_error( $result ) ) { error_log( $result->get_error_message() ); } } ``` ## 4. Send a password reset email You can override the default WordPress password reset email by hooking into `retrieve_password_message`. ```php add_filter( 'retrieve_password_message', 'my_custom_password_reset', 10, 4 ); function my_custom_password_reset( $message, $key, $user_login, $user_data ) { // Send via Sidemail $reset_url = network_site_url( "wp-login.php?action=rp&key=$key&login=" . rawurlencode( $user_login ), 'login' ); sidemail_send_email( array( 'toAddress' => $user_data->user_email, 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your Site', 'templateName' => 'Password Reset', 'templateProps' => array( 'actionUrl' => $reset_url, 'username' => $user_login, ), ) ); // Return false to stop WordPress from sending the default email return false; } ``` ## 5. Send a contact form submission If you are processing a custom contact form: ```php function handle_contact_form_submission() { // ... validate nonce and fields ... $email = sanitize_email( $_POST['email'] ); $name = sanitize_text_field( $_POST['name'] ); sidemail_send_email( array( 'toAddress' => 'admin@yoursite.com', 'fromAddress' => 'system@yoursite.com', 'fromName' => 'Contact Form', 'templateName' => 'New Inquiry', 'templateProps' => array( 'replyTo' => $email, 'name' => $name, 'message' => sanitize_textarea_field( $_POST['message'] ), ), ) ); } ``` ## 6. Send HTML email ```php sidemail_send_email( array( 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your Site', 'subject' => 'Hello from WordPress', 'html' => '

Hello world! 👋

', ) ); ``` ## 7. Schedule email Send email later. Set `scheduledAt` to an ISO date string. ```php $scheduled_at = gmdate( 'c', time() + 3600 ); // 1 hour from now sidemail_send_email( array( 'toAddress' => 'user@email.com', 'fromAddress' => 'you@yourdomain.com', 'fromName' => 'Your Site', 'templateName' => 'Welcome', 'scheduledAt' => $scheduled_at, ) ); ``` --- # Domain verification and DKIM Source: https://sidemail.io/docs/sending-identities/index.md # Domain verification and DKIM In order to send emails from your own domain, you have to verify the domain first by setting up a DNS record. This step is required to ensure you have the right to use the domain. Verify your custom domain in your project's settings. During the verifying process, Sidemail will give you a CNAME DNS record that you need to place in your domain's DNS provider. Typically, it takes just a few minutes for DNS changes to take effect. However, it can occasionally take **up to 72 hours**. **Note:** If your domain's DNS provider is [Cloudflare](https://www.cloudflare.com/), Sidemail offers a direct integration. Simply authenticate through the Sidemail Dashboard, and your DNS records will be configured automatically – no manual copy-pasting required. ## After successful verification After your domain verification with DKIM is complete you can send emails from any address within the verified domain, for example: - `support@{domain}` - `your-name@{domain}` - `project-name@newsletter.{domain}` - `hello@from.more.subdomains.{domain}` --- # Set up onboarding sequence Source: https://sidemail.io/docs/set-up-onboarding-sequence/index.md # Set up onboarding sequence Let's look at how to set up an onboarding sequence for SaaS. The goal is to convert trial users to paid customers. This is a common use-case, and it's easy to set up with Sidemail. We'll look at how to set up: - Trial expiration email sequence (draft pre-made) - Retry trial email sequence (draft pre-made) - Welcome email sequence (start from scratch, easiest to set up) You'll send data about your users to Sidemail via API ⁠— you choose what data. When new data arrive, Sidemail checks whether the new data match automation trigger conditions. If there's a match, Sidemail schedules all emails in the email sequence for delivery. Ok, let's set this up. ### Configure contact profiles You can push any data about your users to Sidemail via API, but first, you have to define the property name and its data type. In your Sidemail project's settings, define the following properties: - `pricingPlan` as `string` - `registredAt` as `date` Use your preferred naming convention (`camelCase`, `snake_case`, etc.) ### Integrate the API Sidemail has a single `create/update` API endpoint, so you don't have to write the handling logic yourself. Send the API request from your backend. ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.contacts.createOrUpdate({ emailAddress: "john.doe@example.com", identifier: "123", // ID representing the user in your database customProps: { pricingPlan: "premium", registeredAt: "2019-08-15T13:20:39.160Z", }, }); ``` ```ruby require "sidemail" sm = Sidemail.new(api_key: "replace-with-your-api-key") sm.contacts.create_or_update( emailAddress: "john.doe@example.com", identifier: "123", customProps: { pricingPlan: "premium", registeredAt: "2019-08-15T13:20:39.160Z", } ) ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.contacts.create_or_update( emailAddress="john.doe@example.com", identifier="123", customProps={ "pricingPlan": "premium", "registeredAt": "2019-08-15T13:20:39.160Z", }, ) ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->contacts->createOrUpdate([ 'emailAddress' => 'john.doe@example.com', 'identifier' => '123', 'customProps' => [ 'pricingPlan' => 'premium', 'registeredAt' => '2019-08-15T13:20:39.160Z', ], ]); ``` ```bash curl -X POST https://api.sidemail.io/v1/contacts \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "emailAddress": "john.doe@example.com", "identifier": "123", "customProps": { "pricingPlan": "premium", "registeredAt": "2019-08-15T13:20:39.160Z" } }' ``` ([Read more about contact profile API](/docs/contact-profiles-quickstart/)) You should place the code that sends the user's data to Sidemail in a strategic place in your server code to keep the users' data up-to-date in Sidemail. For example, in your billing and authentication controllers (or middlewares). Alternatively, you could create a repeating background job that updates users' data in Sidemail every 15 minutes. To verify that your application successfully pushed user data to Sidemail, find appropriate contact in contacts, and you should see when was the last time the contact property was updated and the current value. ### Set up the automation Go to Automation in your Sidemail project, and you'll find two automation drafts we pre-made: "Retry trial" and "7-day trial". Click to edit (or start from scratch by clicking the "+ New automation" button). Set up the following trigger conditions: - `registredAt` -> is now (current hour) "Is now (current hour)" condition means that the automation will only trigger at the current hour when the user registered. Ensure user data are sent to Sidemail when user registers (eg., in registration controller). Next, let's focus on the email message. Click on the message to edit (or click "+Add message" if you're starting from scratch). Adjust the message, subject, and from address to your liking. Keep it simple. You can always edit the message even after you activate the automation. Edit how much time Sidemail should wait before delivering your message after the automation is triggered. The delay is always relative to the previous message, so if you choose to wait 2 days for the first message and 5 days for the second, the second message will be delivered 7 days after the automation was triggered. Next, let's set up the goal. The goal is to convert a user to a paid plan. When the goal is met, Sidemail will automatically delete all remaining emails from this automation that are scheduled for delivery. The goal is optional, so when there isn't necessarily any goal (eg., welcome email), you can leave it empty. Keep in mind, that automation without a goal (or a goal that is impossible to hit) will get triggered multiple times if the data change from trigger-matching to non-trigger-matching back to trigger-matching. For example, leverage this behavior to send email notifications about user's usage/quota that resets every month). Set up the following goal conditions: - `pricingPlan` -> equals -> "premium" (replace with your plan name) If you have multiple plans, create another goal condition with the same property name and condition type, but with the additional plan name as the condition value. Repeat for all plans. Importantly, set "Filters match" to "Any condition". ### Activate the automation You're almost done. In your automation, click the "Activate automation" button. Confirm it. Congratulations! Test your automation. When automation is triggered, you can find all emails scheduled for delivery in your project's History. ### Next steps Next up, you can set up customer retention automation. For example, set up automation to send an email notification when a customer's payment card is about to expire. --- # SMTP Relay Source: https://sidemail.io/docs/smtp-relay/index.md # SMTP Relay Send emails using standard SMTP instead of our HTTP API. Perfect for legacy systems, WordPress plugins, or any app that supports SMTP. ## Connection Settings - **Host:** `mx.sidemail.net` - **Port:** `587` - **Encryption:** STARTTLS - **Username:** Any value (e.g., `api` or empty) - **Password:** Your API key ## Features - **Same deliverability** as the HTTP API - **Verified sender validation** — emails must come from verified addresses - **Attachments supported** — up to 5MB total message size - **CC and BCC** — fully supported - **Markdown support** — send markdown emails via custom header - **Templates and scheduling** — use Sidemail headers for template sends and scheduled delivery ## Markdown Emails To send markdown-formatted emails via SMTP, add the `X-Sidemail-Markdown: true` header to your message. When this header is present, the plain text body is treated as markdown and rendered to HTML. ```javascript const nodemailer = require("nodemailer"); const transporter = nodemailer.createTransport({ host: "mx.sidemail.net", port: 587, secure: false, auth: { user: "api", pass: "your-api-key" } }); await transporter.sendMail({ from: "you@yourdomain.com", to: "customer@example.com", subject: "Weekly Update", text: `# Hello! This is a **markdown** email with: - Bullet points - [Links](https://example.com) - And more!`, headers: { "X-Sidemail-Markdown": "true" } }); ``` ```python import smtplib from email.mime.text import MIMEText markdown_content = """# Hello! This is a **markdown** email with: - Bullet points - [Links](https://example.com) - And more!""" msg = MIMEText(markdown_content) msg["Subject"] = "Weekly Update" msg["From"] = "you@yourdomain.com" msg["To"] = "customer@example.com" msg["X-Sidemail-Markdown"] = "true" with smtplib.SMTP("mx.sidemail.net", 587) as server: server.starttls() server.login("api", "your-api-key") server.send_message(msg) ``` ## Sidemail Headers Use these optional headers to access Sidemail features over SMTP: - `X-Sidemail-Markdown: true` — treat the plain text body as markdown. - `X-Sidemail-Template-Id: 192f1f77bcf86cd799439011` — send a saved template by ID. - `X-Sidemail-Template-Name: Welcome` — send a saved template by name. - `X-Sidemail-Scheduled-At: 2026-05-20T09:00:00.000Z` — schedule delivery with an ISO 8601 date. - `X-Sidemail-Open-Tracking: false` — disable open tracking for this email. When a template header is present, the SMTP message body is not used as the email content. The standard `Subject` header can still be used to override the template subject. ```javascript await transporter.sendMail({ from: "you@yourdomain.com", to: "customer@example.com", subject: "Welcome to our app", headers: { "X-Sidemail-Template-Name": "Welcome", "X-Sidemail-Scheduled-At": "2026-05-20T09:00:00.000Z", "X-Sidemail-Open-Tracking": "false" } }); ``` ## Code Examples ```javascript const nodemailer = require("nodemailer"); const transporter = nodemailer.createTransport({ host: "mx.sidemail.net", port: 587, secure: false, auth: { user: "api", pass: "your-api-key" } }); await transporter.sendMail({ from: "you@yourdomain.com", to: "customer@example.com", subject: "Hello!", text: "This is a test email.", html: "

This is a test email.

" }); ``` ```php isSMTP(); $mail->Host = 'mx.sidemail.net'; $mail->Port = 587; $mail->SMTPAuth = true; $mail->Username = 'api'; $mail->Password = 'your-api-key'; $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS; $mail->setFrom('you@yourdomain.com'); $mail->addAddress('customer@example.com'); $mail->Subject = 'Hello!'; $mail->Body = '

This is a test email.

'; $mail->AltBody = 'This is a test email.'; $mail->send(); ``` ```python import smtplib from email.mime.text import MIMEText msg = MIMEText("This is a test email.") msg["Subject"] = "Hello!" msg["From"] = "you@yourdomain.com" msg["To"] = "customer@example.com" with smtplib.SMTP("mx.sidemail.net", 587) as server: server.starttls() server.login("api", "your-api-key") server.send_message(msg) ``` ## Limits - **5MB** maximum message size (including attachments) - Sender address must be [verified](/docs/sending-identities/) in your project - Inline images referenced with `cid:` URLs are supported when the MIME part includes a matching `Content-ID` ## Troubleshooting ### Authentication failed - Double-check your API key is correct - Make sure the API key belongs to the project you're sending from - Verify your account is active and not suspended ### Sender not verified The "From" address must be verified in your Sidemail project. Add and verify it in your dashboard under Sending Identities. ### Connection timeout - Ensure port 587 is not blocked by your firewall - Try using a different network to rule out ISP blocking --- # Template iteration group Source: https://sidemail.io/docs/template-iteration-group/index.md # Template iteration group You can define dynamic lists in the no-code email editor, and then send an `array` of `objects` inside of `templateProps` parameter from which the dynamic list will be generated. Use-cases: - A website monitoring tool that sends an weekly email report. User can have multiple websites monitored. We'll use this as a example below. - A receipt where there are many options or products a customer can purchase. - A blogging platform that notifies an article owner about new comments. ## Set up email template First, you'll need to create an template in the no-code email editor and fill in a `iteration group name`. All elements support the `iteration group name` and you can find it in element's options. You can use all template props by name `{item}` or `{price}` in any element which has the `iteration group name` specified as `list`. This means template props inside iteration group `array` are made local to each element that is in iteration group. But this also means template props inside of iteration group overwrite global template props (eg., `price` inside `list` would overwrite `price` that is directly inside of `templateProps` parameter), ## Code example ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.sendEmail({ toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "After purchase email", templateProps: { list: [ { item: "🧢 The Shirt Shop Script A Hat", price: "25 USD" }, { item: "👕 Peter Millar Jubilee Game Day Polo", price: "113 USD" }, { item: "📦 2-day shipping", price: "FREE" }, { item: "**Total**", price: "**138 USD**" }, ], }, }); ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ 'toAddress' => 'user@example.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'templateName' => 'After purchase email', 'templateProps' => [ 'list' => [ [ 'item' => '🧢 The Shirt Shop Script A Hat', 'price' => '25 USD' ], [ 'item' => '👕 Peter Millar Jubilee Game Day Polo', 'price' => '113 USD' ], [ 'item' => '📦 2-day shipping', 'price' => 'FREE' ], [ 'item' => '**Total**', 'price' => '**138 USD**' ], ], ], ]); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.send_email( toAddress="user@example.com", fromAddress="you@example.com", fromName="Your app", templateName="After purchase email", templateProps={ "list": [ { "item": "🧢 The Shirt Shop Script A Hat", "price": "25 USD" }, { "item": "👕 Peter Millar Jubilee Game Day Polo", "price": "113 USD" }, { "item": "📦 2-day shipping", "price": "FREE" }, { "item": "**Total**", "price": "**138 USD**" }, ] } ) ``` ```bash curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "After purchase email", "templateProps": { "list": [ { "item": "🧢 The Shirt Shop Script A Hat", "price": "25 USD" }, { "item": "👕 Peter Millar Jubilee Game Day Polo", "price": "113 USD" }, { "item": "📦 2-day shipping", "price": "FREE" }, { "item": "**Total**", "price": "**138 USD**" } ] } }' ``` ## Nested iteration group It's often useful to include a dynamic list inside of dynamic list. You can define nested iteration group by using `.` dot in the element's `iteration group name`. Only one iteration group can be nested inside of another iteration group. For the following code example to work, the element's `iteration group name` should be set to `list.downtimes`. All template props from the `downtimes` array will be made local in the element that is in the iteration group `list.downtimes` (overwriting any global template props). ```js const configureSidemail = require("sidemail"); const sidemail = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sidemail.sendEmail({ toAddress: "user@example.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Weekly report", templateProps: { list: [ { website: "Example app 1", uptime: "99.252", downtimes: [ { date: "2020-05-05", duration: "10" }, { date: "2020-05-07", duration: "23" }, { date: "2020-05-09", duration: "2" }, ], }, { website: "Example website 2", uptime: "99.252", downtimes: [ { date: "2020-05-05", duration: "1000" }, { date: "2020-05-02", duration: "100" }, { date: "2020-05-07", duration: "2300" }, { date: "2020-05-09", duration: "200" }, ], }, // etc... ], }, }); ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ 'toAddress' => 'user@example.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'templateName' => 'Weekly report', 'templateProps' => [ 'list' => [ [ 'website' => 'Example app 1', 'uptime' => '99.252', 'downtimes' => [ [ 'date' => '2020-05-05', 'duration' => '10' ], [ 'date' => '2020-05-07', 'duration' => '23' ], [ 'date' => '2020-05-09', 'duration' => '2' ], ], ], [ 'website' => 'Example website 2', 'uptime' => '99.252', 'downtimes' => [ [ 'date' => '2020-05-05', 'duration' => '1000' ], [ 'date' => '2020-05-02', 'duration' => '100' ], [ 'date' => '2020-05-07', 'duration' => '2300' ], [ 'date' => '2020-05-09', 'duration' => '200' ], ], ], ], ], ]); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.send_email( toAddress="user@example.com", fromAddress="you@example.com", fromName="Your app", templateName="Weekly report", templateProps={ "list": [ { "website": "Example app 1", "uptime": "99.252", "downtimes": [ { "date": "2020-05-05", "duration": "10" }, { "date": "2020-05-07", "duration": "23" }, { "date": "2020-05-09", "duration": "2" }, ], }, { "website": "Example website 2", "uptime": "99.252", "downtimes": [ { "date": "2020-05-05", "duration": "1000" }, { "date": "2020-05-02", "duration": "100" }, { "date": "2020-05-07", "duration": "2300" }, { "date": "2020-05-09", "duration": "200" }, ], }, ] } ) ``` ```bash curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@example.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Weekly report", "templateProps": { "list": [ { "website": "Example app 1", "uptime": "99.252", "downtimes": [ { "date": "2020-05-05", "duration": "10" }, { "date": "2020-05-07", "duration": "23" }, { "date": "2020-05-09", "duration": "2" } ] }, { "website": "Example website 2", "uptime": "99.252", "downtimes": [ { "date": "2020-05-05", "duration": "1000" }, { "date": "2020-05-02", "duration": "100" }, { "date": "2020-05-07", "duration": "2300" }, { "date": "2020-05-09", "duration": "200" } ] } ] } }' ``` --- # Dynamic data with template props Source: https://sidemail.io/docs/template-props/index.md # Dynamic data with template props In the real world, emails are not just static and that's when template props come in handy. Template props are basically variables that allow you to put dynamic data in your email templates. - Template props can be used in any no-code editor's element. - To define a template prop, use `{}` curly brackets. For example, in a password reset email template, you specify the URL of a button to `{url}` or `{passwordResetUrl}` or `{password_reset_url}` depending on your preference. Now when sending the password reset email template, the template expects you to supply `url` (or whatever name you used) inside of `templateProps` object parameter. This is how you send a template with `templateProps`: ```javascript const configureSidemail = require("sidemail"); const sm = configureSidemail({ apiKey: "replace-with-your-api-key" }); const response = await sm.sendEmail({ toAddress: "user@email.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Password reset", templateProps: { "url": "https://reset.me/123" } }); ``` ```ruby require "sidemail" sm = Sidemail.new(api_key: "replace-with-your-api-key") response = sm.send_email( toAddress: "user@email.com", fromAddress: "you@example.com", fromName: "Your app", templateName: "Password reset", templateProps: { url: "https://reset.me/123" } ) ``` ```php $sm = new Sidemail\Sidemail(apiKey: 'replace-with-your-api-key'); $response = $sm->sendEmail([ 'toAddress' => 'user@email.com', 'fromAddress' => 'you@example.com', 'fromName' => 'Your app', 'templateName' => 'Password reset', 'templateProps' => [ "url" => "https://reset.me/123" ] ]); ``` ```python from sidemail import Sidemail sm = Sidemail(api_key="replace-with-your-api-key") resp = sm.send_email( toAddress="user@email.com", fromAddress="you@example.com", fromName="Your app", templateName="Password reset", templateProps={ "url": "https://reset.me/123" } ) ``` ```bash curl -X POST https://api.sidemail.io/v1/emails \ -H "Content-Type: application/json" \ -H "Authorization: Bearer replace-with-your-api-key" \ -d '{ "toAddress": "user@email.com", "fromAddress": "you@example.com", "fromName": "Your app", "templateName": "Password reset", "templateProps": { "url": "https://reset.me/123" } }' ``` When no corresponding template prop name is found inside of `templateProps`, the template prop is left as is, just the curly brackets `{}` are removed. ## Use-cases for template props Here are a few ideas of how you can utilize template props: - to personalize an email with user's first name, eg., `{firstName}` becomes `John` if `templateProps` look like this: ```javascript { "firstName": "John" } ``` - to include unique typically auto-generated URLs (password reset, login link), eg., `{sso_url}` becomes `https://login.me/in?token=123` if `templateProps` look like this: ```javascript { "sso_url": "https://login.me/in?token=123" } ``` - show the charged amount for your service on a receipt, eg., `{charged-price}` becomes `$249` if `templateProps` look like this: ```javascript { "charged-price": "$249" } ``` ## Gotchas `templateProps` object parameter expects the values of template props to be `string`. Make sure you convert numbers to strings, otherwise, Sidemail will return `parameters-invalid` [error](/docs/api/errors/). Data for [chart element](/docs/data-charts/) and [iteration group](/docs/template-iteration-group/) are exceptions. --- # Receive email events via webhooks Source: https://sidemail.io/docs/webhooks/index.md # Receive email events via webhooks Sidemail uses webhooks to notify your application when an event happens in your project. Webhooks are particularly useful for asynchronous events like when an email is delivered, bounces, is opened, when the recipient reports a spam complaint, when your inbound route receives a new email, or when a sending domain is verified or fails verification. ## How to set up - In your project's API section, add a webhook URL, choose which events it should receive, and get your unique webhook secret. - In your webhook handler, compare the value of `sidemail-secret` header with your webhook secret to verify the request. - Respond to the webhook request with 200 status code (response with any other status is recognized as a webhook failure). ## Node.js webhook handler example: ```js function handleSidemailWebhook(req, res, next) { // Get your webhook secret in your project's API section const WEBHOOK_SECRET = "replace-with-your-webhook-secret"; // Verify that the request is coming from Sidemail if (req.headers["sidemail-secret"] !== WEBHOOK_SECRET) { return res.status(401).send(); } // Your custom logic ... console.log(req.body.type, req.body.data); // Acknowledge the webhook by responding with 200 status code return res.status(200).send(); } ``` ## Events Domain events are organization-wide because sending domains are shared across projects. If multiple projects in the organization have webhook URLs configured, each project webhook with that domain event enabled receives it. ### Event: `email.delivered` ```js { "type": "email.delivered", "data": { "email": { "id": "123", "templateId": "123", "templateName": "Welcome", "templateProps": { "name": "John" }, "fromName": "Example App", "fromAddress": "hello@example.com", "toAddress": "user@example.com", "createdAt": "2020-11-07T09:50:09.951Z" }, "time": "2020-11-07T09:50:09.951Z", "smtpResponse": "250 2.6.0 Message received" } } ``` ### Event: `email.open` Triggered on the first open of an email by the recipient. Subsequent opens are not reported. ```js { "type": "email.open", "data": { "email": { "id": "123", "templateId": "123", "templateName": "Welcome", "templateProps": { "name": "John" }, "fromName": "Example App", "fromAddress": "hello@example.com", "toAddress": "user@example.com", "createdAt": "2020-11-07T09:50:09.951Z" }, "time": "2020-11-07T09:50:09.951Z", "ipAddress": "203.0.113.10", "userAgent": "Mozilla/5.0 ..." } } ``` ### Event: `email.bounce` ```js { "type": "email.bounce", "data": { "email": { "id": "123", "templateId": "123", "templateName": "Welcome", "templateProps": { "name": "John" }, "fromName": "Example App", "fromAddress": "hello@example.com", "toAddress": "user@example.com", "createdAt": "2020-11-07T09:50:09.951Z" }, "time": "2020-11-07T09:50:09.951Z", "bounceType": "permanent" } } ``` ### Event: `email.complaint` ```js { "type": "email.complaint", "data": { "email": { "id": "123", "templateId": "123", "templateName": "Welcome", "templateProps": { "name": "John" }, "fromName": "Example App", "fromAddress": "hello@example.com", "toAddress": "user@example.com", "createdAt": "2020-11-07T09:50:09.951Z" }, "time": "2020-11-07T09:50:09.951Z", "complaintFeedbackType": "abuse" } } ``` ### Event: `email.received` Triggered when an inbound route receives an email. ```js { "type": "email.received", "data": { "email": { "id": "email_123456", "self": "https://api.sidemail.io/v1/inbound/emails/email_123456", "destination": "hi@example.com", "from": { "email": "sender@example.com", "name": "Sender" }, "to": [{ "email": "support@example.com", "name": "Support Team" }], "cc": [{ "email": "ops@example.com", "name": null }], "replyTo": [{ "email": "billing@example.com", "name": "Billing" }], "subject": "Example inbound message", "text": "Hello, please find invoice attached...", "htmlAvailable": true, "attachments": [ { "name": "attachment.pdf", "contentType": "application/pdf", "size": 84320 } ], "previewHtmlUrl": "https://api.sidemail.io/v1/inbound/emails/preview-html?token=...", "rawEmailUrl": "https://storage.example.com/..." }, "spam": { "score": 2.4, "threshold": 15, "isSpam": false, "action": "accept", "symbols": ["R_SPF_ALLOW", "R_DKIM_ALLOW", "MIME_GOOD"] }, "time": "2026-03-27T12:01:18.921Z" } } ``` Notes: - `spam.symbols` contains the rule names that contributed to spam scoring. - `email.self` is a stable API URL for fetching full inbound email detail. - `destination` is the SMTP recipient routed in Sidemail. Header recipients are in `to` and `cc`. - `previewHtmlUrl` is present only when an HTML part is available and is short-lived (currently 15 minutes). - `rawEmailUrl` is a short-lived signed URL (currently 5 minutes). - `text` is truncated to 10 000 characters in the webhook. Fetch the full body via `email.self`. - `attachments` contains metadata only (name, content type, size in bytes). Download the raw email via `rawEmailUrl` to access attachment content. ### Event: `domain.sending.success` Triggered when a sending domain changes to verified and is ready for sending. ```js { "type": "domain.sending.success", "data": { "domain": { "id": "123", "domain": "example.com", "status": "success", "previousStatus": "pending", "mailFromDomain": "out.mail.example.com", "mailFromStatus": "success", "createdAt": "2026-05-10T09:50:09.951Z" }, "time": "2026-05-10T10:05:09.951Z" } } ``` ### Event: `domain.sending.failed` Triggered when a sending domain changes to failed. ```js { "type": "domain.sending.failed", "data": { "domain": { "id": "123", "domain": "example.com", "status": "failed", "previousStatus": "pending", "mailFromDomain": "out.mail.example.com", "mailFromStatus": "failed", "createdAt": "2026-05-10T09:50:09.951Z" }, "time": "2026-05-13T09:50:09.951Z" } } ```