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

# Connect to Campaigns

> Hand entries from automation logic into email, in-app, and campaign-style follow-up.

Automations reach users through three messaging nodes. Each decides **who** it sends to using the same `recipientMode` setting, so the patterns below apply to all of them.

## Choosing recipients

<ParamField path="recipientMode" type="string">
  `triggering_profile` (default) — send to the contact who entered the automation.
  `wallet_list` — send to a fixed list of wallets.
</ParamField>

<ParamField path="walletAddresses" type="string[]">
  Required when `recipientMode` is `wallet_list`. Maximum 250 per node.
</ParamField>

`triggering_profile` is what you want almost always — it's what makes the automation personal. Use `wallet_list` for announcements to a known set of wallets.

<Note>
  There is no segment-wide fan-out inside a flow. An automation processes **one contact per entry**. To reach a whole segment, either send a campaign to that segment directly, or have your system post one `segment_entered` event per contact.
</Note>

## Send an email

```json theme={null}
{
  "id": "email_welcome",
  "type": "send_email",
  "data": {
    "templateId": "tpl_welcome",
    "subject": "Welcome to the community",
    "recipientMode": "triggering_profile"
  }
}
```

`templateId` is required. `subject` overrides the template's own subject when present.

Email steps **skip rather than fail** when delivery isn't possible:

| Situation                             | Result                                    |
| ------------------------------------- | ----------------------------------------- |
| Every recipient suppressed            | Node skipped, reason `all_suppressed`     |
| Sending reputation blocked            | Node skipped, reason `reputation_blocked` |
| Some sends succeed                    | Node succeeds                             |
| Zero sends succeed for another reason | Node fails and retries                    |

The flow continues past a skipped node, so an automation can complete without sending anything. If an email step produced no messages, check entry events for `node_skipped` before assuming the trigger is broken.

## Send an in-app push

```json theme={null}
{
  "id": "inapp_welcome",
  "type": "send_inapp",
  "data": {
    "title": "Welcome onchain",
    "body": "Thanks for minting. Here's what to do next.",
    "ctaLabel": "Explore",
    "ctaUrl": "https://app.example.com/start",
    "recipientMode": "triggering_profile"
  }
}
```

`title` and `body` are required unless you point the node at a campaign to draw content from. Both `ctaLabel` and `ctaUrl` must be present for a button to render — a label alone is dropped.

In-app pushes reach wallets that have authenticated through the SDK. Offline users receive the push on their next connect, within the 72-hour retention window. See [In-App Notifications](/integrations/in-app-notifications) for the client setup, and note that in-app messages draw from the **same monthly allowance as email**.

Wallets with no in-app session are skipped. To size the reachable audience ahead of time, query your segment with `?reachable_on=inapp`.

## Dispatch a campaign

```json theme={null}
{
  "id": "handoff",
  "type": "dispatch_campaign",
  "data": {
    "campaignId": "cmp_q3_winback",
    "recipientMode": "triggering_profile"
  }
}
```

Use this when the follow-up content is owned by the campaigns team and maintained on their schedule, rather than embedded in your flow.

## Handoff patterns

<AccordionGroup>
  <Accordion title="Immediate in-app, delayed email" icon="bolt">
    Acknowledge on-chain instantly where the user already is, then follow with email once the moment has passed.

    `trigger → send_inapp → wait 1h → send_email`

    This is the shape of the built-in **Mint Welcome** play.
  </Accordion>

  <Accordion title="Channel fallback" icon="code-branch">
    Prefer in-app, fall back to email when the wallet has no session.

    `trigger → branch (walletAddress exists → send_inapp | else → send_email)`

    Branch conditions read the rolling context, so `walletAddress` and `email` are both available. See [Triggers and Conditions](/automation/triggers-and-conditions).
  </Accordion>

  <Accordion title="Nudge after intent" icon="clock">
    Someone approved a token but never swapped. Wait, then prompt.

    `trigger (approval_intent) → wait 30m → send_email → send_inapp`

    The built-in **Approval Abandoned Cart** play.
  </Accordion>

  <Accordion title="Tag now, campaign later" icon="tag">
    Don't message immediately — mark the contact so a campaign can pick them up.

    `trigger → add_tag → webhook`

    Useful when send timing belongs to the campaigns team rather than the trigger.
  </Accordion>
</AccordionGroup>

## Measuring the handoff

Automation stats report entries, conversions, conversion rate, and revenue, with time-series and per-path breakdowns.

<Warning>
  Conversion and revenue attribution are derived by pattern-matching event names and scanning delivery metadata for revenue-shaped keys. They're directional indicators, not an accounting source. Reconcile against your own analytics before reporting numbers externally.
</Warning>

## Keeping teams aligned

Agree on the intended path before building. The recurring failure is two systems owning the same moment — an automation and a scheduled campaign both messaging the same wallets on the same trigger. Write down which system owns which moment, and who owns the audience.
