> ## 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.

# Weekly report

> Every Monday morning, gather last week's numbers and email them to your team.

A weekly report is simple work that must happen on time, every week: count the sign-ups, the orders, and the revenue, then send the summary. Run it from a server's crontab, and it's skipped the week that server is down, or sent twice when two servers run it.

With Nelkap, the schedule lives with the workflow. Nelkap starts one run each Monday, whatever runs your app and however many instances it has. Each run gathers the numbers in steps, so a run cut off halfway goes on without counting again.

![The runs of sendWeeklyReport, one each Monday at 9:00 UTC, all completed.](https://mintcdn.com/holotus/-VkqCXx08dglIr0L/images/weekly-report-light.png?fit=max\&auto=format\&n=-VkqCXx08dglIr0L\&q=85\&s=4a02b3bd9299d15797cda9b621978255)

## Write the workflow

```ts nelkap/workflows/send-weekly-report.ts theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
import { sendReportEmail } from '../../lib/email';
import {
  countOrders,
  countSignups,
  startOfLastWeek,
  sumRevenue,
} from '../../lib/metrics';
import { nelkap } from '../client';

export async function sendWeeklyReport() {
  const weekStart = await nelkap.step.run('week start', () =>
    startOfLastWeek(new Date()).toISOString(),
  );

  const [signups, orders, revenue] = await Promise.all([
    nelkap.step.run('signups', () => countSignups(weekStart)),
    nelkap.step.run('orders', () => countOrders(weekStart)),
    nelkap.step.run('revenue', () => sumRevenue(weekStart)),
  ]);

  await nelkap.step.run('email', () =>
    sendReportEmail(
      { weekStart, signups, orders, revenue },
      `report_${weekStart}`,
    ),
  );
}
```

A scheduled run has no payload, so the workflow takes no argument:

1. It works out when last week started. The date is a step, so every call of the run reports on the same week, even one that picks up after midnight.
2. It counts the sign-ups, the orders, and the revenue side by side, as three steps saved on their own.
3. It emails the report, with an idempotency key built from the week, so the email goes out once even if a crash cuts the step off before it's saved.

Replace the functions from `lib` with your own queries and your email provider.

## Schedule

Register the workflow with its schedule:

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

nelkap.registerWorkflows([
  {
    name: 'sendWeeklyReport',
    handler: sendWeeklyReport,
    cron: '0 9 * * 1',
  },
]);

export { nelkap, sendWeeklyReport };
```

`0 9 * * 1` starts a run every Monday at 9:00 UTC. Once your app has registered it, nothing else in your code starts the report. See [Schedules](https://docs.nelkap.com/triggers/schedules.md#cron-syntax) to write your own.

## Test it

You don't have to wait for Monday. Start a run from any server code of your app:

```ts theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
await nelkap.run(sendWeeklyReport);
```

It runs the same workflow, and the report goes out within a minute.

## View the runs

Filter your project's runs by the workflow: each Monday shows one run, with Cron as its trigger. Open one to see the week it covered, the three counts, and the email.

If Nelkap couldn't start runs for a while, the Mondays missed meanwhile give a single run, so your team never gets a pile of reports at once.

## Going further

* Record the week's conversion rate with [`step.score`](https://docs.nelkap.com/reference/step-score), to chart it week after week.
* Send each customer their own report: list them in a step, and start a run for each one. See [Fan-out](https://docs.nelkap.com/guides/loops.md#fan-out).


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