> ## Documentation Index
> Fetch the complete documentation index at: https://docs.urbackend.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Send mail

> Send transactional email with direct content or a named template.

## POST `/api/mail/send`

Sends transactional email from your project.

<Info>
  For the complete Mail Platform documentation (BYOK, batch sending, logs, audiences, contacts, broadcasts, webhooks, and dashboard workflows), see <a href="/guides/mail-platform">Mail Platform guide</a>.
</Info>

<Warning>
  This endpoint requires a **Secret Key** (`sk_live_...`). Do not call it from browser/client code.
</Warning>

### Required headers

| Header         | Value                         |
| :------------- | :---------------------------- |
| `x-api-key`    | Your `sk_live_...` secret key |
| `Content-Type` | `application/json`            |

### Request body

<ParamField body="to" type="string | string[]" required>
  Recipient email address, or an array of recipient addresses. Each address must be a valid email. An array must contain at least one entry.
</ParamField>

<ParamField body="replyTo" type="string | string[]">
  Optional reply-to address, or an array of addresses. Each address must be a valid email. When provided as an array, it must contain at least one entry. Forwarded to Resend as the message's `Reply-To` header.
</ParamField>

<ParamField body="subject" type="string">
  Required in direct mode.
</ParamField>

<ParamField body="html" type="string">
  Optional HTML body. In direct mode, provide at least one of `html` or `text`.
</ParamField>

<ParamField body="text" type="string">
  Optional text body. In direct mode, provide at least one of `html` or `text`.
</ParamField>

<ParamField body="templateId" type="string">
  Template ObjectId (24 hex chars). Use either `templateId` or `templateName`.
</ParamField>

<ParamField body="templateName" type="string">
  Template key or name. Use either `templateId` or `templateName`.
</ParamField>

<ParamField body="variables" type="object">
  Optional template variables.
</ParamField>

### Validation rules

* Template mode: `templateId` or `templateName`
* Direct mode: `subject` + (`html` or `text`)
* Do not pass both `templateId` and `templateName`.
* `to` and `replyTo` must contain valid email addresses. Array forms cannot be empty (`Recipient list cannot be empty`, `Reply-to list cannot be empty`).
* Subject is required after any template resolution and variable interpolation (`Subject is required`).
* The rendered message must include a non-empty `html` or `text` body. Requests where both come out empty after interpolation are rejected with `Provide html or text` (or `Provide at least one of html or text content.` when neither field was supplied).

### Template resolution order

When using `templateName`, urBackend resolves in this order:

1. Project template override
2. Global/system template
3. Legacy embedded template fallback (older projects only)

### Placeholder syntax

* Escaped: `{{name}}`
* Raw: `{{{htmlSnippet}}}` (use carefully)
* Nested: `{{user.name}}`

For URL values, prefer escaped placeholders like `{{appUrl}}`. URL-like variables are sanitized to safe `http/https` values.

### How to use

<Steps>
  <Step title="Get your Secret Key">
    Log in to the [urBackend Dashboard](https://urbackend.bitbros.in/dashboard), create a new project, and copy your **Secret Key** (`sk_live_...`) or you can get it from rotating it from project overview page.
  </Step>

  <Step title="Copy the Code">
    Copy your preferred language snippet from below. Use **Direct Mode** if you just want to send a quick HTML/Text email without setting up templates or use welcome template for fast test.
  </Step>

  <Step title="Paste and Run">
    Paste the code into your app, replace `sk_live_YOUR_SECRET_KEY` with your actual key, change the `to` email address, and run it!
  </Step>
</Steps>

<Info>
  The Java examples use the native `java.net.http.HttpClient` (requires **Java 11+**) and Text Blocks for JSON formatting (requires **Java 15+**).
</Info>

<CodeGroup>
  ```javascript fetch (template mode) theme={null}
  const res = await fetch('https://api.ub.bitbros.in/api/mail/send', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': 'sk_live_YOUR_SECRET_KEY'
    },
    body: JSON.stringify({
      to: 'user@example.com',
      templateName: 'welcome',
      variables: {
        name: 'Ava',
        projectName: 'Acme',
        appUrl: 'https://acme.com'
      }
    })
  });
  ```

  ```javascript fetch (direct mode) theme={null}
  const res = await fetch('https://api.ub.bitbros.in/api/mail/send', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': 'sk_live_YOUR_SECRET_KEY'
    },
    body: JSON.stringify({
      to: 'user@example.com',
      replyTo: 'support@acme.com',
      subject: 'Welcome to Acme',
      html: '<h1>Hello</h1><p>Your account is ready.</p>'
    })
  });
  ```

  ```java Java (template mode) theme={null}
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;

  public class Main {
    public static void main(String[] args) throws Exception {
      String jsonBody = """
        {
          "to": "user@example.com",
          "templateName": "welcome",
          "variables": {
            "name": "Ava",
            "projectName": "Acme",
            "appUrl": "https://acme.com"
          }
        }
        """;

      HttpRequest request = HttpRequest.newBuilder()
          .uri(URI.create("https://api.ub.bitbros.in/api/mail/send"))
          .header("Content-Type", "application/json")
          .header("x-api-key", "sk_live_YOUR_SECRET_KEY")
          .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
          .build();

      HttpClient client = HttpClient.newHttpClient();
      HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
      System.out.println(response.body());
    }
  }
  ```

  ```java Java (direct mode) theme={null}
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;

  public class Main {
    public static void main(String[] args) throws Exception {
      String jsonBody = """
        {
          "to": "user@example.com",
          "subject": "Welcome to Acme",
          "html": "<h1>Hello</h1><p>Your account is ready.</p>"
        }
        """;

      HttpRequest request = HttpRequest.newBuilder()
          .uri(URI.create("https://api.ub.bitbros.in/api/mail/send"))
          .header("Content-Type", "application/json")
          .header("x-api-key", "sk_live_YOUR_SECRET_KEY")
          .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
          .build();

      HttpClient client = HttpClient.newHttpClient();
      HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
      System.out.println(response.body());
    }
  }
  ```
</CodeGroup>

### Success response

```json theme={null}
{
  "success": true,
  "data": {
    "id": "mail_123",
    "provider": "default",
    "monthlyUsage": 1,
    "monthlyLimit": 100
  },
  "message": "Mail sent successfully."
}
```

### Errors

| Status                  | Message                                                             | Cause                                                                  |
| :---------------------- | :------------------------------------------------------------------ | :--------------------------------------------------------------------- |
| `400 Bad Request`       | `Invalid recipient email format` / `Recipient list cannot be empty` | `to` is missing, malformed, or an empty array                          |
| `400 Bad Request`       | `Invalid replyTo email format` / `Reply-to list cannot be empty`    | `replyTo` is malformed or an empty array                               |
| `400 Bad Request`       | `Subject is required`                                               | Subject missing, blank, or empty after template/variable interpolation |
| `400 Bad Request`       | `Provide at least one of html or text content.`                     | Neither `html` nor `text` supplied (or both empty after interpolation) |
| `400 Bad Request`       | `Template not found.`                                               | `templateId` / `templateName` does not resolve                         |
| `401 Unauthorized`      | `Project context missing.`                                          | Request has no resolved project                                        |
| `403 Forbidden`         | `Forbidden. This action requires a Secret Key (sk_live_...).`       | Non-secret key used                                                    |
| `404 Not Found`         | `Project not found.`                                                | Project record missing                                                 |
| `429 Too Many Requests` | `Monthly mail limit exceeded.`                                      | Project quota exhausted for the month                                  |


## Related topics

- [Mail](/sdk/mail.md)
- [September 2026](/changelog/september-2026.md)
- [Mail Platform](/guides/mail-platform.md)
- [SDK Overview](/sdk/overview.md)
