<Header icon={<img src="/docs/logos/resend.svg" className="w-9 h-9 inline" />}>Resend</Header>

[Resend](https://resend.com) is an email API for developers. This destination syncs your Jitsu users to Resend
as **contacts**, so you can email or broadcast to them and manage their segment membership.

## How it works

Resend identifies contacts by **email address** — a contact is a global entity keyed by its email. Resend has
no concept of a `userId`, so email is the only stable key. Jitsu maps events to Resend contacts as follows:

| Event      | What Jitsu does                                                                                   |
|------------|---------------------------------------------------------------------------------------------------|
| `identify` | Creates the contact if new, updates it otherwise, and reconciles its audience membership.          |
| `track` / `page` / `screen` | Updates the existing contact's custom properties (never creates a contact).      |
| `group`    | Updates the existing contact with the group id and group traits as `group_*` properties.          |

### identify

Each `identify()` upserts a contact:

* **Email** is read from `traits.email` (falling back to `context.traits.email`). Events without an email are
  skipped — Resend requires an email.
* **First / last name** come from `traits.firstName` / `traits.lastName`, or are split from `traits.name`.
* **`unsubscribed`** is set from `traits.unsubscribed` when it is a boolean.
* **All other traits** are stored as Resend contact [properties](https://resend.com/docs/dashboard/audiences/introduction)
  (custom fields). Resend properties are strings, so non-string values are coerced (objects are JSON-encoded).
  The Jitsu `userId` and `anonymousId` are stored as `jitsu_user_id` and `jitsu_anonymous_id`.
  Resend requires every property to be defined before it can be set, so Jitsu automatically creates a
  string-typed property definition for each new trait key the first time it sees it.
* The contact's [audience](#audiences) membership is reconciled to match the configured set.

A repeated `identify()` for the same email **updates** the existing contact rather than creating a duplicate.

### Other events

`track`, `page` and `screen` events **update** an existing contact but never create one. An update happens only
when the event carries custom contact fields (typically added by a
[transformation function](/docs/functions)) — a plain event with just an email does nothing. The email is
resolved from the event's traits, or from the `userId` cache (see [below](#matching-events-by-userid)). If no
contact matches, the event is skipped.

`group` events fold the `groupId` and group traits into the contact's properties, namespaced as `group_<key>`.
They don't change audience membership.

## Audiences

Set **Audiences** in the destination config to a **comma-separated list of audience names** (not IDs) — for
example `Customers, Beta`. Jitsu resolves each name to a Resend audience, **creating any that don't exist yet**.
Leave it empty to add contacts without an audience.

You can override the audiences per event by setting a `resendAudiences` trait (also comma-separated names) from
a [function](/docs/functions):

```javascript
export default async function (event) {
  if (event.traits?.plan === "enterprise") {
    event.traits.resendAudiences = "Customers, Enterprise";
  }
  return event;
}
```

### Membership is reconciled

On each `identify()`, Jitsu makes the contact's membership match the desired set. Audiences it **previously
added** that are no longer listed are **removed**. For example, if a function returns `A, B` on one event and
`B, C` on the next, the contact is removed from `A`, kept in `B`, and added to `C`.

To do this safely, the connector records which audiences it added in a contact property named
`jitsu_managed_audiences` — so it only ever removes audiences **it** added, never ones you added manually or via
another tool. There's no separate state stored in Jitsu; the record lives on the contact.

Notes:

* Reconciliation runs only when a desired set is provided for the event — i.e. the `resendAudiences` trait is
  present, or the destination has configured Audiences. An event with neither leaves membership untouched.
* Setting `resendAudiences` to an empty string explicitly clears the connector-managed audiences.
* Only `identify()` manages membership; `track` / `page` / `group` events never change it.

## Matching events by userId

Resend cannot look a contact up by `userId` — only by email or Resend's own contact id. So by default, only
events that carry an email are matched to a contact.

Enable **Resolve email from userId** to change this. When on, every `identify()` caches a `userId → email`
mapping, and later `track` / `page` / `group` events that carry only a `userId` are matched to the contact via
that cache. Notes:

* The cache is only populated from `identify()` calls seen **after** the flag is enabled — historical mappings
  are not backfilled.
* If neither an email nor a cached `userId` resolves, the event is skipped (no contact is created or updated).

## Configuration

<DestinationConfiguration type="resend" />