---
name: azion-send-transactional-email-with-resend-and-functions
description: >-
  Build a Hono endpoint on Azion Functions that calls the Resend API, store the API key as an environment variable, and deploy it with Azion CLI.
---

# Send transactional email with Resend and Functions

In this tutorial, you will build an HTTP endpoint on Azion Functions that sends a transactional email through Resend. You will create the project from a template, write the handler, store the API key, deploy the project, and send a live message.

The finished project is in the [edge-functions-examples repository](https://github.com/egermano/edge-functions-examples/tree/main/packages/resend).

---

## Prerequisites

- An Azion account. To create one, refer to [How to create an account on Azion](/en/documentation/fundamentals/creating-account/).
- A [Resend](https://resend.com/) account with an API key.
- Azion CLI installed. For the installation options, refer to [Azion CLI](/en/documentation/devtools/cli/).
- Node.js version 18 or higher.

---

## 1. Create the project from the Hono template

To authorize the CLI and create the project:

1. **Authenticate Azion CLI**

   Run the login command:

   ```bash
   azion login
   ```

   With no credential flags, the CLI opens a browser-based flow. It stores the credentials locally, and every later command is authorized against your account.

2. **Initialize the project**

   Run the init command in the directory that holds your projects:

   ```bash
   azion init
   ```

   The command opens an interactive journey.

3. **Name the project**

   Enter a name, or press `enter` to accept the suggestion:

   ```sh
   ? Your application's name:  (black-thor)
   ```

4. **Select the Hono preset**

   The CLI lists the presets it supports:

   ```sh
   ? Choose a preset:  [Use arrows to move, type to filter]
     Angular
     Astro
     Docusaurus
     Eleventy
     Emscripten
     Gatsby
     Hexo
   > Hono
     Hugo
     Javascript
     ...
   ```

5. **Select the Hono Boilerplate template**

6. **Decline the local server and the deployment**

   The CLI offers a local development server and a deployment. Answer `N` to both, because the handler is not written yet:

   ```sh
   Do you want to start a local development server? (y/N)
   Do you want to deploy your project? (y/N)
   ```

7. **Install the project dependencies**

   Go to the project directory and install its dependencies:

   ```bash
   cd <your-project-name>
   npm install
   ```

The project directory holds the Hono template and its dependencies.

---

## 2. Write the handler that calls the Resend API

The handler answers a `GET` request on the root path, posts the message to the Resend API, and returns the API response as JSON. The `entry` field of `azion.config.js` names the entry file of the project. Open that file and replace its contents with:

```typescript
import { Hono } from "hono";

const app = new Hono();

const RESEND_API_KEY = Azion.env.get("RESEND_API_KEY")!;
const EMAIL_TO = "<your-email-address>";

app.get("/", async (c) => {
  const res = await fetch("https://api.resend.com/emails", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${RESEND_API_KEY}`,
    },
    body: JSON.stringify({
      from: "onboarding@resend.dev",
      to: EMAIL_TO,
      subject: "hello world",
      html: "<strong>it works!</strong>",
    }),
  });

  const data = await res.json();

  return c.json(data);
});

export default app;
```

Set `EMAIL_TO` to the address that receives the message. The `html` field carries the message body as an HTML string, so a template literal substitutes the values that change per recipient.

The final line exports the app as an ES Modules handler, the pattern Azion recommends over the legacy Service Worker pattern. For both patterns and the move between them, refer to [Migrate handler patterns in Functions](/en/documentation/guides/application-development/functions-and-runtime/migrate-handler-patterns/).

---

## 3. Store the Resend API key as an environment variable

The handler reads `RESEND_API_KEY` at run time, so the key stays out of the project files and out of version control. To create the variable on your account:

```bash
azion create variables --key "RESEND_API_KEY" --value "[TOKEN VALUE]" --secret true
```

A variable whose key contains `password`, `pwd`, `secret`, `key`, `hash`, `encrypted`, `passcode`, `auth`, or `token` is sent as a secret by default. The `--secret true` flag repeats that default here. Give the Resend API key the smallest permission set that the send call needs.

To replace the value later, run `azion update variables` and deploy the project again. A change to a variable does not reach a function that is already running.

The account holds the variable, and the handler reads it by key after the deployment.

---

## 4. Deploy the project

To build the project and publish it:

```bash
azion deploy
```

The deployment uploads the function code, configures the application, sets the routing rules, applies the environment variables and secrets, and returns a domain in the format `https://xxxxxxx.map.azionedge.net`. Azion opens a browser at the Azion Console page that carries the deployment logs.

> **Caution**
>
> The deployed endpoint is public, and every request to it sends a message. Before the handler takes the recipient or the body from the request, validate and sanitize both fields, require authentication on the route, and limit its request rate.

The endpoint answers on the returned domain a few minutes after propagation.

---

## 5. Send the first email

To run the handler, request the root path of the domain the deployment returned:

```bash
curl https://<your-azion-domain>/
```

The response body is the JSON that the Resend API returns for the request. The message arrives in the inbox of the address set in `EMAIL_TO`, with the subject `hello world` and the body `it works!`.

> **Note**
>
> If the response carries an error from Resend, confirm that `RESEND_API_KEY` exists on your account and that the project was deployed after the variable was created. For a function that does not run, stops early, or writes an error to its logs, refer to [Troubleshoot function execution and logs](/en/documentation/platform/functions/troubleshooting/).

The endpoint now sends a transactional email on every request to its root path.

---

## Next steps

- [Environment variables](/en/documentation/platform/functions/environment-variables.md): The three fields of a variable, the interface that reads one, and the CLI subcommands that manage them.
- [Migrate handler patterns in Functions](/en/documentation/guides/application-development/functions-and-runtime/migrate-handler-patterns.md): The ES Modules and Service Worker handlers, their parameters, and how to move a function between them.
- [Functions limits](/en/documentation/platform/functions/limits.md): The CPU time, wall-clock time, and sub-request ceilings that one invocation runs inside.
- [Functions best practices](/en/documentation/platform/functions/best-practices.md): What each part of the handler does, and the design choices that keep an invocation inside its limits.
- [How to build with Hono](/en/documentation/guides/application-development/frameworks/hono.md): The full azion init journey for a Hono project, including the local development server.
- [Resend API](https://resend.com/docs): The fields the Resend emails endpoint accepts, and the rest of its API.
