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

# Set up Loops in Next.js

> How to send email from your Next.js project with Loops.

This guide shows how to add Loops to your Next.js project, so you can send transactional emails, manage contacts and trigger automated emails.

## Install the SDK

The first step is to install the [Loops SDK](/sdks/javascript). This is written in TypeScript so you can benefit from strict types when coding.

```bash theme={"dark"}
npm i loops
```

<Tip>
  You can install [Loops skills](/skills) to help your coding agents use the Loops CLI, API and SDKs to create campaigns, manage contacts, send events, and send transactional emails.
</Tip>

You'll need an API key to use the SDK. Go to your [API Settings page](https://app.loops.so/settings?page=api) in Loops to generate and copy a key.

Save this value in your environment variables as something like `LOOPS_API_KEY`.

Then you can import the Loops SDK client like this:

```javascript theme={"dark"}
import { LoopsClient } from "loops";

const loops = new LoopsClient(process.env.LOOPS_API_KEY);
```

<Tip>
  You can also use the [Loops API](/api-reference/intro) directly in your app, without
  the SDK. [Read more](#using-the-api-instead)
</Tip>

<CardGroup>
  <Card title="JavaScript SDK" href="/sdks/javascript" icon="js">
    Explore our official JS/TS SDK.
  </Card>

  <Card title="Loops API" icon="rectangle-terminal" href="/api-reference/intro">
    Read the Loops API reference.
  </Card>
</CardGroup>

## Server-side only

It is important that you only use the Loops API and SDK from server-side code. If you make calls directly in the browser, you risk exposing your API key, which would give other people read and write access to your Loops account data. Additionally, the Loops API does not support cross-origin requests made from client-side JavaScript.

If you want to make calls from the browser—for example, to collect newsletter subscriptions from a form—create proxy endpoints. To add a new contact, create an internal API endpoint and use the Loops API/SDK within it.

```typescript app/api/contacts/route.ts theme={"dark"}
import { NextRequest, NextResponse } from "next/server";
import { LoopsClient } from "loops";

const loops = new LoopsClient(process.env.LOOPS_API_KEY as string);

export async function POST(request: NextRequest) {
  const res = await request.json();

  const email = res["email"];

  // Note: updateContact() will create or update a contact
  const resp: {
    success: boolean;
    id?: string;
    message?: string;
  } = await loops.updateContact(email);

  return NextResponse.json({ success: resp.success });
}
```

## Send transactional email

A big use case for using Loops in a Next.js project is to send transactional email to users. These emails are one-off emails, which help users with your product, for example password reset emails notification emails.

To create a transactional email, go to the Transactional page in Loops. Click **Create** or select a template.

Create the email in [the editor](/creating-emails/editor), which gives you rich formatting options and components.

<img src="https://mintcdn.com/loops/aOtgyczWtTw7x5Xz/images/transactional-example.png?fit=max&auto=format&n=aOtgyczWtTw7x5Xz&q=85&s=deed77ee6f7c34086b8f26cce1518ddb" alt="Creating a transactional email" width="2280" height="1355" data-path="images/transactional-example.png" />

To add dynamic content (like rest password URLs or user data) you can add [data variables](/transactional#add-data-variables) into the email from the toolbar.

Give each data variable a unique name. You can populate these variables from your code when sending the email via the SDK in the next step.

Make sure to Publish your transactional email when you're done.

Now your email is created you can start sending emails.

In your code, call `sendTransactionalEmail()` and include values for each of the data variables you added to your email.

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  const dataVariables = {
    loginUrl: "https://myapp.com/login/",
  };

  const resp = await loops.sendTransactionalEmail({
    transactionalId: "transaction_email_id",
    email: "test@example.com",
    dataVariables
  });

  if (!resp.success) {
    // The sending failed
  } else {
    // The email was sent successfully
  }

  ```

  ```typescript TypeScript theme={"dark"}
  const dataVariables: { loginUrl: string } = {
    loginUrl: "https://myapp.com/login/",
  };

  const resp: {
    success: boolean,
    path?: string,
    message?: string
  } | {
    success: false;
    error: {
      path: string;
      message: string;
    };
    transactionalId?: string;
  } = await loops.sendTransactionalEmail({
    transactionalId: "transaction_email_id",
    email: "test@example.com",
    dataVariables
  });

  if (!resp.success) {
    // The sending failed
  } else {
    // The email was sent successfully
  }
  ```
</CodeGroup>

The response will contain a `success` boolean telling you if the email was sent successfully. If it was not, you'll also receive an error message.

<CardGroup className="mt-8">
  <Card title="sendTransactionalEmail()" href="https://github.com/loops-so/loops-js#sendtransactionalemail" icon="code">
    Read more in the SDK docs.
  </Card>
</CardGroup>

## Sync users to Loops

Another main use case for teams using Loops is to keep their Loops audience updated when user data changes in their application.

To do this you can use the `updateContact()` method.

<Tip>
  `updateContact()` can be used as a shortcut "update or create" function. It
  will create new contacts if the provided email address and/or user ID are not
  found.
</Tip>

For example, you may store custom data in Loops like subscription plan level or user usage information that you include in emails. You can update contacts in Loops like this:

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  const contactProperties = {
    userId: 826,
    planName: "Pro" /* Custom property */,
    usage: 172629 /* Custom property */,
  };

  const resp = await loops.updateContact("test@example.com", contactProperties);

  if (!resp.success) {
  // The call failed
  } else {
  // The contact was updated OK
  }

  ```

  ```typescript TypeScript theme={"dark"}
  const contactProperties: Record<string, string | number> = {
    userId: 826,
    planName: "Pro" /* Custom property */,
    usage: 172629 /* Custom property */,
  };

  const resp: {
    success: boolean,
    id?: string,
    message?: string
  } = await loops.updateContact("test@example.com", contactProperties);

  if (!resp.success) {
    // The call failed
  } else {
    // The contact was updated OK
  }
  ```
</CodeGroup>

The TypeScript example above shows how to properly type your `contactProperties` object and the expected response from the `updateContact` method.

<Tip>
  We recommend always populating the `userId` value for users, which should be
  their unique value in your platform. This allows you to change a contact's
  email address in the future, because they have a separate unique identifier in
  the system.
</Tip>

<CardGroup className="mt-8">
  <Card title="updateContact()" href="https://github.com/loops-so/loops-js#updatecontact" icon="user">
    Read more in the SDK docs.
  </Card>
</CardGroup>

## Trigger workflows with events

A third example of using the Loops SDK is to trigger [workflows](/workflows). Workflows are automated email sequences, which can send multiple emails to contacts.

You can trigger these emails using [events](/events), and you can send events to Loops using the SDK.

For example, you may have a workflow that you send to new users after they have completed an onboarding flow in your app.

First, [create a new workflow](https://app.loops.so/loops) using the "Event received" trigger. Add emails, timers and audience filters to your workflow as you wish.

Then to trigger this email sequence, send an event to Loops. If your event name is `completedOnboarding`, your call would look like this...

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  const resp = await loops.sendEvent({
    email: "test@example.com",
    eventName: "completedOnboarding",
  });

  if (!resp.success) {
  // The event was not sent
  } else {
  // The event was sent OK
  }

  ```

  ```typescript TypeScript theme={"dark"}
  const resp: {
    success: boolean,
    message?: string,
  } = await loops.sendEvent({
    email: "test@example.com",
    eventName: "completedOnboarding",
  });

  if (!resp.success) {
    // The event was not sent
  } else {
    // The event was sent OK
  }
  ```
</CodeGroup>

<CardGroup className="mt-8">
  <Card title="sendEvent()" href="https://github.com/loops-so/loops-js#sendevent" icon="bolt">
    Read more in the SDK docs.
  </Card>
</CardGroup>

## Using the API instead

If you prefer, you can use the [Loops API](/api-reference/intro) directly instead of using the SDK.

<Warning>
  You should never call the API from your front-end code as this will expose
  your API key.
</Warning>

For example, you can send a transactional email like this:

```javascript theme={"dark"}
const data = {
  email: "test@example.com",
  transactionalId: "abcdefg",
  dataVariables: {
    loginUrl: "https://myapp.com/login/?code=1234",
  },
};
return fetch("https://app.loops.so/api/v1/transactional", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.LOOPS_API_KEY}`,
  },
  body: JSON.stringify(data),
})
  .then((response) => response.json())
  .then((response) => {
    if (!response.success) {
      // The sending failed
    } else {
      // The email was sent successfully
    }
  })
  .catch((err) => console.error(err));
```

<Tip>
  On Vercel, each backend function gets its own lambda. Make sure you use
  `return` otherwise the lambda might be terminated before the promise is
  evaluated.
</Tip>

<CardGroup>
  <Card title="API Reference" icon="rectangle-terminal" href="/api-reference/intro">
    Read through our API documentation.
  </Card>
</CardGroup>
