> For the complete documentation index, see [llms.txt](https://docs.nerovasystems.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nerovasystems.com/guides/operate-the-digital-employee/notifications.md).

# Send customer notifications

Send business-initiated WhatsApp messages outside an open conversation through pre-approved templates: reminders, follow-ups, and updates.

Your platform explicitly requests a deterministic notification with `POST /api/v1/tenants/{tenantId}/notifications` and a supported `type`. The request remains subject to API-key tenant scope, actor, idempotency, consent, and channel requirements. This is not a native scheduled campaign or an AI event/intent API.

Supported WhatsApp template messages can be used outside an open customer-service window. A template definition or approval alone is not proof that a message was delivered.

{% hint style="info" %}
**Experimental.** The `/api/v1/**` operations on this page are callable with your Live key (`nrv_live_`), which runs on the free testing allowance until a commercial agreement is signed or a card is saved. See [Lifecycle and availability](https://docs.nerovasystems.com/documentation/api/lifecycle).
{% endhint %}

## What you'll build

An explicit notification call from your platform: choose the supported type, fill its fields, and submit idempotently. If your platform schedules that request, the scheduler is yours, not a retained Nerova native campaign.

## Prerequisites

{% hint style="info" %}

* An activated tenant whose WhatsApp channel is ready; check [channels](https://docs.nerovasystems.com/documentation/concepts/channels) if unsure.
* An API key with `notification:send`; see [Authentication](https://docs.nerovasystems.com/documentation/getting-started/authentication).
  {% endhint %}

## 1. Pick the type and its fields

These are examples of the deterministic types and their fields; the [Notifications reference](https://docs.nerovasystems.com/api-reference/notifications) lists every supported type:

| Type          | Required fields                                     |
| ------------- | --------------------------------------------------- |
| `reminder`    | `clientName`, `businessName`, `serviceName`, `when` |
| `no_show`     | `clientName`, `serviceName`                         |
| `cancelled`   | `clientName`, `serviceName`, `when`                 |
| `rescheduled` | `clientName`, `serviceName`, `when`, `newWhen`      |
| `update`      | `clientName`, `businessName`, `message`             |

Nerova sends no payment messages. The payment reminder, receipt, late-cancel fee, and no-show fee notification types (`payment_reminder`, `receipt`, `late_cancel_fee`, `no_show_fee`) are retired, along with the `paymentUrl` and `feeAmount` fields. A request that uses one of them is rejected as an unknown type. The matching customer WhatsApp templates (payment reminder, invoice due, payment failed, refund processed, late-cancel fee, no-show fee, and receipt) are retired too, as are the quote approval, on-my-way, ready-for-pickup, subject recall, and renewal notice templates. Handle payments in your own platform. See [First-release scope](https://docs.nerovasystems.com/documentation/console/platform-access#first-release-scope).

The [local source retirement](https://docs.nerovasystems.com/documentation/console/platform-access#campaign-and-escalation-retirement) also removes the weekly-digest, vaccination-due, service-due, win-back, and fill-slot customer definitions and native campaign execution. Historical deferred preferences stay off until explicitly enabled with their old mode cleared. Unknown or retired message keys fail closed; do not use deleted timing or delivery-mode controls to revive them.

Every template field is limited to 300 characters and is collapsed to a single line before rendering. `to` is the customer's phone number in international format, 8–15 digits with an optional `+` prefix.

## 2. Send it idempotently

Key the request on your own event identity so a retried job never sends the same notification twice:

{% tabs %}
{% tab title="curl" %}

```bash
curl --request POST \
  --url "https://api.nerovasystems.com/api/v1/tenants/{tenantId}/notifications" \
  --header "Accept: application/json" \
  --header "Authorization: ******" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: remind-booking-8412-2026-02-06" \
  --data '{
    "to": "+27821234567",
    "type": "reminder",
    "clientName": "Jane",
    "businessName": "Studio Luma",
    "serviceName": "Balayage",
    "when": "Friday 6 Feb, 10:00",
    "actor": {
      "actorReference": "system:reminder-scheduler",
      "delegationReference": null
    }
  }'
```

{% endtab %}

{% tab title="C#" %}

```csharp
// client construction: see the .NET SDK page (docs.nerovasystems.com/sdks/dotnet)
using Nerova.Sdk.Models;

var tenantId = "1539251826400956416";
var action = await client.Api.V1.Tenants[tenantId].Notifications.PostAsync(
    new SendPartnerNotificationCommand
    {
        To = "+27821234567",
        Type = "reminder",
        ClientName = "Jane",
        BusinessName = "Studio Luma",
        ServiceName = "Balayage",
        When = "Friday 6 Feb, 10:00",
        Actor = new PartnerRuntimeActorRequest
        {
            ActorReference = "system:reminder-scheduler"
        }
    },
    requestConfiguration => requestConfiguration.Headers.Add(
        "Idempotency-Key", "remind-booking-8412-2026-02-06"));

Console.WriteLine($"{action?.State} ({action?.IdempotencyStatus})");
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
// client construction: see the TypeScript SDK page (docs.nerovasystems.com/sdks/typescript)
const tenantId = "1539251826400956416";
const action = await client.api.v1.tenants.byTenantId(tenantId).notifications.post(
  {
    to: "+27821234567",
    type: "reminder",
    clientName: "Jane",
    businessName: "Studio Luma",
    serviceName: "Balayage",
    when: "Friday 6 Feb, 10:00",
    actor: { actorReference: "system:reminder-scheduler" },
  },
  { headers: { "Idempotency-Key": "remind-booking-8412-2026-02-06" } },
);

console.log(`${action?.state} (${action?.idempotencyStatus})`);
```

{% endtab %}
{% endtabs %}

A `200` returns `"state": "sent"` with an `idempotencyStatus` of `applied` for a new command or `replayed` for a repeat of one already processed. That command state is not proof that the recipient read the message.

## 3. Handle the failure modes

| Status | Code                                     | What to do                                                                                                               |
| ------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `400`  | `partner.notification_invalid`           | Fix the `type`, the missing `actor`, or the template field that is missing or over 300 characters                        |
| `400`  | `partner.notification_recipient_invalid` | Correct the `to` phone number                                                                                            |
| `409`  | `partner.channel_not_ready`              | The WhatsApp channel cannot send; check [incidents](/guides/observe-and-account/incidents.md) and retry once it recovers |
| `409`  | `partner.action_outcome_unknown`         | Delivery could not be verified; check the conversation transcript before acting again                                    |

The rendered text is recorded on the customer's conversation transcript, so you can confirm what was sent with [Read and steer conversations](/guides/operate-the-digital-employee/conversations.md).

## Next steps

* Retry-safe patterns for every mutation: [Handle errors and retries](/guides/build-well/error-handling.md).
* React to platform events without polling: [Receive events with webhooks](/guides/build-well/webhooks.md).
* Full request and response shapes: [Notifications reference](https://docs.nerovasystems.com/api-reference/notifications).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.nerovasystems.com/guides/operate-the-digital-employee/notifications.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
