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

# Onboarding emails

> Welcome each new user, then send tips three days later to those who haven't created a project.

A good onboarding series sends the right email at the right time: a welcome the moment someone signs up, and a nudge a few days later, only if they haven't started. Without durable execution, that takes a queue, a scheduled job that scans your users every day, and a table to remember who got which email.

With Nelkap, the whole series is one function that reads from top to bottom. Each new user gets their own run, which sleeps for three days between the two emails without holding your app.

![An onboarding run: the welcome email on day 1, a three-day sleep, then the project count and the tips email on day 4.](https://mintcdn.com/holotus/zu12qw3F64kRzbLL/images/welcome-emails-light.png?fit=max\&auto=format\&n=zu12qw3F64kRzbLL\&q=85\&s=ecbefac368ffd353a70937324923be50)

## Write the workflow

```ts nelkap/workflows/send-onboarding-emails.ts theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
import { sendEmail } from '../../lib/email';
import { countProjects } from '../../lib/projects';
import { nelkap } from '../client';

export async function sendOnboardingEmails(user: { id: string; email: string }) {
  await nelkap.step.run('welcome email', () => sendEmail(user.email, 'welcome', `welcome_${user.id}`));

  await nelkap.step.sleep('3 days');

  const projectCount = await nelkap.step.run('project count', () => countProjects(user.id));

  if (projectCount === 0) {
    await nelkap.step.run('tips email', () => sendEmail(user.email, 'tips', `tips_${user.id}`));
  }
}
```

The workflow takes the new user as its payload:

1. It sends the welcome email. The step is saved as soon as the email is sent, so a later call never sends it again.
2. It sleeps for three days. The call ends there, and nothing runs in your app until Nelkap calls it back.
3. It counts the user's projects. The count is a step too, so it's saved: if the run picks up again right after, it keeps the same count instead of asking again.
4. If the user has no project yet, it sends the tips email.

Replace `sendEmail` and `countProjects` with your own functions. Here, `sendEmail` takes the address, the template, and an idempotency key that your email provider uses to ignore a second send. That way, an email goes out once even if a crash cuts the step off before it's saved. See [When a step can run twice](https://docs.nelkap.com/concepts/steps.md#when-a-step-can-run-twice).

## Start it at each sign-up

Register the workflow with the event that starts it:

```ts nelkap/index.ts theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
import { nelkap } from './client';
import { sendOnboardingEmails } from './workflows/send-onboarding-emails';

nelkap.registerWorkflows([
  { name: 'sendOnboardingEmails', handler: sendOnboardingEmails, events: ['user.signed_up'] },
]);

export { nelkap };
```

Then emit the event in your sign-up code, once the user is created:

```ts theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
await nelkap.emit({ event: 'user.signed_up', payload: { id: user.id, email: user.email } });
```

Each sign-up now starts a run, with the user as its payload. Another workflow that should react to sign-ups, like one that adds the user to your CRM, can listen for the same event without any change to your sign-up code.

## See it run

Open the run in your project's runs. Right after the sign-up, the welcome email step shows what it returned, and the run is sleeping. Three days later, the run counts the projects, sends the tips if needed, and completes.

If a user deletes their account in the meantime, cancel their run from its page, and the tips email never goes out.

## Ideas to extend it

* Add more emails, each after its own sleep. The series stays in one function, in the order the user receives it.
* Tag each run with the user's plan, to filter the runs by plan later.
* Record whether the user had a project after three days with [`step.score`](https://docs.nelkap.com/reference/step-score), to chart how well your onboarding works over time.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.