--- title: "Send Transactional Emails via API V1" slug: "send-transactional-emails-api" description: "Insider One's Transactional Email API reference: POST endpoint to send one-to-one emails from an authorized domain, rate-limited to 9,000 requests/second." tags: ["transactional email", "transactional email api", "email api"] updated: 2026-10-07T15:40:10Z published: 2026-10-07T15:40:10Z canonical: "academy.insiderone.com/send-transactional-emails-api" --- > ## Documentation Index > Fetch the complete documentation index at: https://academy.insiderone.com/llms.txt > Use this file to discover all available pages before exploring further. # Send Transactional Emails via API V1 > [**V2**](/docs/transactional-emails) is now available. If you are planning to integrate with Insider One's transactional email API, use the newer V2 endpoint. The following are available in V2: > > - [Liquid](/docs/liquid-tags-in-email) syntax for personalization > - Conditional content and item lists (loops) > - View in Browser > - Analytics categories, for reporting by email type > - Template validation before sending > - Delivery-status tracking > > V1 remains supported until August 2027. This API lets you send targeted ecommerce and personalized one-to-one emails, such as order confirmations, receipts, or password-reset emails, to your users. You can trigger these emails once a user takes a specific action on your platform, for cases such as: - Post-purchase emails with purchase or booking details - Password reset confirmations - Order confirmations - Delivery tracking notifications > **Domain authorization is required.** The domains from which you send transactional emails must be authorized. If you have questions about domain authorization, or want to authenticate more domains, [create a ticket](/v1/docs/create-a-support-ticket) for the Insider One's team. If you try to send from an unauthorized domain, you will receive an error and your emails **cannot be sent**. ## Supported transactional email events and their parameters | Event | Event Parameters | | --- | --- | | txn_email_delivered | ip_address, src, subject, message_id | | txn_email_open | ip_address, src, subject, user_agent, message_id | | txn_email_click | ip_address, src, subject, user_agent, url, url_offset_index (number), url_offset_type, message_id | | txn_email_bounce | ip_address, reason, src, subject, type, message_id | | txn_email_blocked | ip_address, reason, src, subject, type | | txn_email_dropped | reason, src, subject, message_id | > Unless otherwise noted, a parameter's type is **string**; any exception is marked next to the parameter. ## Endpoint and headers > **POST** https://mail.useinsider.com/mail/v1/send [](https://developers.useinsider.com/#e3871fa8-8c1a-4379-b95b-04ed0fd2d90f) > Visit our Postman collection to test this request. ### **Headers** | Header | Sample Value | Description | | --- | --- | --- | | X-INS-AUTH-KEY | 1a2b3c4d5e6f | This key is required to authorize your request. Refer to [API Authentication Tokens](https://academy.insiderone.com/docs/api-authentication-tokens) to generate your token. | | Content-Type | application/json | This header specifies the media type of the resource. | | cache-control | no-cache | This header specifies browser caching in requests and responses. | ## Body parameters The table below lists the request body parameters. | Parameter | Description | Data Type | Required | | --- | --- | --- | --- | | subject | Subject line that is sent to the recipient | String | Yes | | tos | Email address(es) to send the email to | Array | Yes | | tos.name | User name | String | No | | tos.email | Email address of the recipient | String | No | | tos.uuid | UUID of the recipient | String | No | | from | Sender information object | Object | Yes | | from.name | Sender name | String | No | | from.email | Sender email | String | No | | content | Email content array | Array | Yes | | content.type | Type of your email content | String | No | | content.value | Email content | String | Yes | | cc | Carbon-copy recipients | Array | No | | bcc | Blind carbon-copy recipients | Array | No | | reply_to | Reply-to address and name | Object | No | | attachments | Any attachments in the email | Array | No | | attachments.content | Base64 encoded content of the attachment | String | No | | attachments.file_name | Attached file name | String | No | | attachments.disposition | Set to inline to embed an image | String | No | | attachments.content_id | Embedded image ID | String | No | | dynamic_fields | Personalization fields, e.g., first_name or product_name1. | Object | No | | unique_args | Tracking arguments for transactional emails. | Object | No | | callback | Webhook event endpoint object. | Object | No | | callback.url | Callback endpoint URL | String | No | | callback.secret | Secret key used to sign the webhook body | String | No | | template_id | ID of a transactional email template | String | No | > If the UUID is the identifier and you want to log transactional email events in the Unified Customer Database (UCD), you need to use a different parameter in the Transactional Email API payload. > > - Insider has recently added the UUID parameter as a body parameter for the Transactional Email API. > - After you send the request via the Transactional Email API, the Insider Email Team will query the UCD for the profile associated with that UUID using the Profile API. Since each UUID needs to be fetched from the Profile API, the email will be sent to the recipients one by one unlike the current structure that is written under the "to" body parameter. > - If a profile with the UUID exists in the UCD, the email address ("em") of that profile will be retrieved from the UCD. The Transactional Email will be sent to that profile’s email address, and the event will be recorded in the UCD based on the UUID. > - If a profile with the given UUID does not exist in UCD, the transactional email will still be sent to the email address provided in the request. However, the event will not be recorded in UCD for that email address because no profile matching the UUID was found. > - If you are using email as an identifier, you don’t need to utilize the UUID parameter. ## Sample Request Send the following JSON body with a POST request to the endpoint above, including the listed headers. Replace the sample values in the request header(s) and body where required before sending your request. ```json {  "subject": "Hello",  "tos": [    {      "name": "John Doe",      "email": "sample@useinsider.com",      "uuid": "1234567"    }  ],  "from": {    "name": "Posh Street",    "email": "posh@street.com"  },  "content": [    {      "type": "text/html",      "value": "

I am {{first_name}}.

"    }  ],  "reply_to": {    "name": "Reply to",    "email": "sample.reply@useinsider.com"  },  "dynamic_fields": {    "first_name": "John",    "last_name": "Doe",    "product_name1": "Laptop",    "price1": "$1000"  },  "unique_args": {    "category": "Confirmation Email",    "purchase_channel": "App"  },  "attachments": [    {      "content": "aW5zaWRlciB0ZXN0",      "file_name": "file.txt"    }  ],  "callback": {    "url": "yourcallbackurl",    "secret": "yoursecret"  } } ``` ### Requests with a template Reference a saved template by template_id. Templates are managed in the [Email Template Library](https://academy.insiderone.com/docs/email-template-library#transactional-email-templates). ```json {  "subject": "Hello",  "tos": [    {      "name": "John Doe",      "email": "sample@useinsider.com"    }  ],  "from": {    "name": "Posh Street",    "email": "posh@street.com"  },  "template_id": "275",  "dynamic_fields": {    "first_name": "John",    "last_name": "Doe"  } } ``` ## Sample responses For all sample responses and error codes, refer to the [Error Codes](/docs/transactional-email-error-codes). #### 202 Accepted This response indicates that your request was successfully completed. ```json {    "message_id": "8e664ecd-352d-4dbd-9786-ceb78b5b173b",    "status_message": "accepted" } ``` #### 400 Bad Request This response indicates that your request is missing the email address in the 'to' parameter. ```json {  "errors": [    "Missing 'email' on 'tos' parameter"  ],  "status_message": "failed to validate request" } ``` #### 400 Bad Request This response indicates that the request has an invalid callback URL. ```json {  "errors": [    "Invalid callback URL in 'url' on 'callback'"  ],  "status_message": "failed to validate request" } ``` This error is triggered for cases like the following: > "url": "domain path" "url": "htp:/domain" "url": "example .com" "url": "" You need to review and correct the URL. The correct usage can be as follows: ```plaintext "callback": {   "url": "https://insiderone.com" } ``` There must be only one URL, and it must be valid and properly formatted. #### 400 This response indicates that the bytes in dynamic fields exceed the maximum allowed limit. ```json {  "status_message": "dynamic fields total byte size exceeds the limit of 49300 bytes" } ``` #### 400 Missing Content Type This response indicates that the request's Content-Type header is incorrect. ```json {  "message": "content type must be application/json",  "status": 400 } ``` #### 400 Missing Subject Line This response indicates that the request is missing the 'subject' parameter. ```json {  "errors": [    "Missing 'subject' parameter"  ],  "message": "bad-request",  "status": 400 } ``` #### 400 Missing Content This response indicates that the request is missing the 'content' parameter. ```json {  "errors": [    "Missing 'content' parameter"  ],  "message": "bad-request",  "status": 400 } ``` #### 400 Missing 'from' Parameter This response indicates that the request is missing the 'from' parameter. ```json {  "errors": [    "Missing 'from' parameter"  ],  "message": "bad-request",  "status": 400 } ``` #### 400 Missing 'to' Parameter This response indicates that the request is missing the 'to' parameter. ```json {  "errors": [    "Missing 'to' parameter"  ],  "message": "bad-request",  "status": 400 } ``` #### 400 Bad Request This response indicates that your request contains too many dynamic fields. SendGrid limits emails to **150 dynamic fields**. ```json {    "status_message": "parameter 'dynamic_fields' can have size of '150' at most" } ``` #### 400 This response indicates that the customer uses more bytes in the dynamic fields body parameter. ```json { "status_message": "dynamic fields total byte size exceeds the limit of 49300 bytes" } ``` #### 400 This response indicates that the total payload limit, including HTML and attachments, **exceeds 30 MB**. ```json { "status_message": "" } ``` #### 401 Invalid API Key This response indicates that the API key provided in the request is invalid, expired, or revoked. ```json {  "message": "The provided API key is invalid, expired or revoked",  "status": 401 } ``` First, check the request headers. Another user may have deleted the key. Verify in the panel that the API key is still active (including whitelist checks, if applicable). If the key no longer exists, [generate a new API key](/v1/docs/api-authentication-tokens). If a newly generated key still does not work, you should contact the Insider One team. #### 401 Unauthenticated Domain This response indicates that your domain is not authenticated. ```json {  "message": "The domain you are trying to use is not authenticated. Please create a support ticket for the Insider Support Team to authenticate it.",  "status": 401 } ``` If the Insider One team has enabled the Transactional Email Domain Authentication for your account, you can send only from the specific authorized domains. During onboarding, customers must register their sending domains via [Add More Domains](https://academy.insiderone.com/docs/email-channel-setup#21-domain-submission). This reduces security risks related to unauthorized domain usage. Before opening a support ticket, verify that the sender domain is properly verified. During onboarding, you must ensure the domain has been authenticated and verified in SendGrid. Otherwise, you might risk spam or block issues. #### 413 Payload Limit Exceeded This response indicates that the email payload size exceeds the maximum allowed limit. ```json {  "status_message": "Email payload limit exceeded" } ``` #### 500 Internal Server Error This response indicates that an internal error occurred on the server side and the request could not be processed. #### 502 Bad Gateway This response indicates a temporary server-side or network-level issue that prevented the request from being processed. ```json {  "status_message": "Temporary gateway failure. The request was not processed.",  "timestamp": 1726944657 } ``` #### 503 Service Unavailable This response indicates that the service is temporarily unavailable due to server overload or maintenance. In this case, you should check whether other recent requests show similar patterns. As the system may temporarily require scaling, you must retry requests within a 15-minute window (503 can occur at any moment). If 503 errors continue for more than 15 minutes, contact the Insider One team with sample requests. #### 504 Server Timeout This response indicates that the server did not receive a timely response from an upstream server. > If a request with the same content is sent for the same user within 15 minutes, the system will return the following message: "status_message": "This request has already been received and is being processed. Any duplicate content from the user will be ignored for the next 15 minutes." This response is typically caused by system load, but upstream service latency may also trigger it. In this case, you should check the overall 504 ratio across other requests. To avoid message loss, perform retries within 15 minutes. The duplicate-prevention mechanisms ensure users won’t receive the same message twice. If 504 errors continue for more than 15 minutes, contact the Insider One team with sample request details. #### 520 Cloudflare Network Error This response indicates an unexpected error on the Cloudflare network side, preventing the request from being processed. > **Suggested Retry Mechanism:** If any of the above errors are encountered (502, 503, 504, 520), the request was not processed successfully on the server side. A retry mechanism is recommended, distributing retries randomly within a 5-minute interval to allow server resources to recover and to prevent accidental duplicate sends Even if a request returns **502** or **504**, processing may still complete in the background. When the same payload is retried, the API may respond with: - **202** Accepted → treated as a new valid request, or - **208** Already Processed → an identical request body was received in the **last 15 minutes**. **208** does **not confirm** that the earlier request was queued. #### 208 – Already Processed This status code indicates that a byte-identical request body was received in the **last 15 minutes**. Repeating it will **not trigger** a new send, but a **208** does **not confirm** that the earlier request was queued. ```json {  "message_id": "dce90e71-6786-4d69-93eb-5162a2508e15",  "status_message": "This request has already been received and is being processed. Any duplicate content from the user will be ignored for the next 15 minutes.",  "timestamp": 1762942657 } ``` This response does **not** indicate that the message was dropped, does **not** relate to suppression logic (unsubscribes, bounces, or spam complaints), or does **not** prevent message_id creation for valid sends; each successfully processed send still receives its own message_id. When this response returns, you can try the following handling: - **Duplicate requests within 15 minutes** The duplicate check compares the raw request body byte for byte; a change in whitespace or field order makes it a new request. If you resend an invalid payload unchanged within 15 minutes, you receive **208** instead of the original **400**, so change the body when testing. If the same transactional email payload is sent twice within a 15-minute deduplication window, the second request is excluded and returns a 208 response with a body such as the following: ```json {  "status_message": "This request has already been received and is being processed. Any duplicate content from the user will be ignored for the next 15 minutes" } ``` - **Retry after temporary Gateway Failure (502 → 208)** If a request failed with **502**, retry it. The retry returns **202** or **208**. A **208** does **not confirm** that the email was queued: V1 records the duplicate check before it validates and queues the request, so if the first attempt failed after that point, an identical retry returns **208** for **15 minutes**, and nothing is sent. Check [Transactional Email Analytics](/v1/docs/transactional-email-analytics), and if the email is missing after 15 minutes, send again. - **General idempotency handling (Same payload handling)** Even if two requests fail or succeed differently, 208 may be used to prevent the same payload from being processed twice. The API uses idempotency logic so that identical requests do not create multiple sends. ## Limitations - All functions must be executed with a simple **HTTPS POST** request. - Only new emails can be sent via this API. No data can be retrieved. - The **API Key** should be provided as the authorization key in the **request header**. If the key is incorrect, the operation will not be executed, and an authorization error will return in the response. - The total size limit for dynamic fields is **49300 bytes**. - This API is designed exclusively for **transactional** use cases and must **not** be used for **bulk** or **promotional** emailing. - The default rate limit is **9000 requests** per second per Inone account. Higher limits can be configured on demand by contacting Insider. > The default limit shown here is a standard baseline. If your use case requires higher capacity, feel free to reach out to the Insider One team. We can adjust it to fit your needs. ## Related - [Webhooks for Transactional Emails](/email-webhooks-for-transactional-emails.md) - [API Authentication Tokens](/api-authentication-tokens.md) - [API Rate Limits](/api-rate-limits.md) - [API Status and Error Codes](/api-status-and-error-codes.md) - [Insider One APIs in Postman](/insider-apis-in-postman.md)