# Plan and run campaigns

Use a configured `WhatsAppClient` from [Install and verify](/docs/npm-whatsapp/install). Examples below assume it is named `whatsapp`. Application callbacks such as `db`, `myDatabase`, and `queue` are supplied by your app; `fileBlob` is the Blob you intend to upload.

## Campaigns

Campaigns are side-effect-free until you call `run`. You bring the database, queue, and logging.

```ts
const plan = whatsapp.campaigns.createPlan({
  template: {
    name: "order_update",
    language: { code: "en_US" },
  },
  recipients: [
    { id: "1", phone: "201234567890", lastContactedAt: Date.now() - 48 * 60 * 60 * 1000 },
    { id: "2", phone: "201111111111", lastContactedAt: Date.now() },
  ],
  rules: {
    messagesPerSecond: 10,
    skipRecentlyContacted: true,
    recentContactWindowMs: 24 * 60 * 60 * 1000,
    maxRetries: 3,
  },
});

const summary = await whatsapp.campaigns.run(plan, {
  async logResult(result) {
    await db.campaignLogs.insert(result);
  },
  async markContacted(recipient, timestamp) {
    await db.contacts.update(recipient.id, { lastContactedAt: timestamp });
  },
});
```

## Bring Your Own Database

This package intentionally does not ship database tables or storage assumptions. Use adapters and callbacks to connect your own stack:

```ts
await whatsapp.campaigns.run(plan, {
  sendTemplate: (recipient, template) => whatsapp.messages.sendTemplate(recipient.phone, template),
  logResult: (result) => myDatabase.writeCampaignResult(result),
  markContacted: (recipient, timestamp) => myDatabase.updateContact(recipient.id, { timestamp }),
  wait: (ms) => queue.sleep(ms),
});
```

Webhook handlers follow the same model: save inbound messages, statuses, media IDs, and template updates wherever your application stores them.

## Execution boundary

`createPlan` prepares work without sending messages. `run` executes sends. The package does not provide durable queue storage or database transactions. Persist results and contact timestamps in the callbacks, and make your application’s retry processing idempotent.