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

# Notifications

> Subscribe to presentation events via webhooks or email, send presentation emails to homeowners, and track opens in real time.

Notifications let you know when something happens during a presentation — a viewer opens it, watches a slide, books an appointment, or signs a contract. You subscribe to specific event types and AI Presentations delivers them to your webhook endpoint, email inbox, or console log. AI Presentations can also send the presentation link to homeowners on your behalf and emit events when the email is sent, opened, or fails to reach the inbox.

## Event types

AI Presentations emits these events:

| Event type            | Fired when                                                                                                                                                                                             |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `presentation.opened` | A viewer opens a presentation link                                                                                                                                                                     |
| `slide.viewed`        | A viewer leaves a slide (includes duration and a skipped flag)                                                                                                                                         |
| `cta.clicked`         | A viewer clicks a call-to-action button                                                                                                                                                                |
| `contract.signed`     | A viewer signs a contract                                                                                                                                                                              |
| `question.asked`      | A viewer asks a question and the AI answers confidently                                                                                                                                                |
| `question.escalated`  | A viewer asks a question the AI could not confidently answer (either it deflected to a human callback, or it hedged with a low-confidence answer). Emitted **instead of** `question.asked`, never both |
| `appointment.booked`  | A viewer books an appointment                                                                                                                                                                          |
| `email.sent`          | AI Presentations sends a presentation email to a homeowner                                                                                                                                             |
| `email.opened`        | A homeowner opens a presentation email (reported by SendGrid)                                                                                                                                          |
| `email.bounced`       | The homeowner's mail server rejected or blocked the presentation email                                                                                                                                 |
| `email.dropped`       | SendGrid suppressed the email before sending, for example because the address previously bounced or unsubscribed                                                                                       |
| `email.complained`    | The homeowner marked the presentation email as spam                                                                                                                                                    |
| `email.unsubscribed`  | The homeowner unsubscribed from your emails                                                                                                                                                            |

Each event includes the `presentationId` and relevant context. For example, `slide.viewed` includes the slide ID and how long the viewer spent on it. AI Presentations records every slide exit, including fast flips. A view under 2 seconds carries `skipped: true`, so you can measure which slides viewers skip and filter skips out of dwell-time metrics. Both `email.sent` and `email.opened` include the delivery attempt ID (`deliveryId`) so you can correlate opens back to a specific send.

Event payloads carry identifiers, never contact details. No event payload includes a homeowner's email address, phone number, or name. The one exception is `question.asked` / `question.escalated`, whose payloads include the question and answer text for 90 days (see [Event retention](#event-retention)). When you query the event log, `email.sent` entries include a top-level `recipientEmail` field alongside the payload so you can still see who received each send.

The four email failure events (`email.bounced`, `email.dropped`, `email.complained`, `email.unsubscribed`) carry the `presentationId`, the delivery attempt ID (`deliveryId`), and SendGrid's message and event identifiers (`sgMessageId`, `sgEventId`). `email.bounced` adds a `bounceType` (`bounce` or `blocked`) and SendGrid's bounce `classification`; `email.dropped` adds SendGrid's drop `reason` (for example `Bounced Address` or `Unsubscribed Address`). These payloads never include the recipient's email address. Email notifications for these events resolve the affected recipient for you at send time.

`question.escalated` carries the same payload shape as `question.asked` plus an `escalationReason` field: `no_answer` when the AI deflected to a human callback, or `low_confidence` when the AI answered but flagged the answer as uncertain. Any given question emits exactly one of `question.asked` or `question.escalated`, so a single question never triggers two notifications.

The `question.asked` payload can also include an optional `degradedReason` field: `no_client`, `request_failed`, `empty_response`, or `invalid_response`. It is set when the viewer received a canned answer because of a temporary issue on AI Presentations's side, not a gap in your content. These answers never emit `question.escalated`, so no one on your team is asked to follow up. The field is additive and optional, so existing consumers are unaffected.

#### Deck and slide identity on question events

`question.asked` and `question.escalated` carry four optional fields that identify the template and slide the question was asked on: `deckId`, `deckSlideId`, `slideTitle`, and `slideIndex` (the slide's 0-based position, the same base as `slide.viewed`'s `slideIndex`). The payload's `slideId` identifies a slide within a single presentation, so it can't be compared across presentations. `deckId` and `deckSlideId` can, which lets you answer questions like "which slide draws the most questions across this template".

AI Presentations resolves these fields on the server from the presentation itself, never from the request. Each field is omitted when it can't be resolved — for example, `deckId` is absent once the template has been deleted. Questions asked outside a presentation (in the template editor or preview) carry none of these fields.

These fields ship as payload version 2. The webhook envelope's `event_version` is `2` for events that carry them and `1` for older entries, where the fields were never recorded. The change is additive: version 1 payloads are unchanged, and existing consumers keep working without modification.

## Create a subscription

AI Presentations provisions a default set of email subscriptions for your organization the first time a user signs in, so the standard "someone opened your presentation" and "someone asked your AI a question" emails start working without any setup. When new event types ship, they're added to existing organizations on the next sign-in and inherit the org's prevailing posture — if you've muted every other email, the new type is muted too.

To add or change recipients, open the **Settings** tab in the template editor. You can also create subscriptions via the API.

Each subscription needs:

* **Name** — a label for your reference, e.g. "Slack — new presentation opened"
* **Event type** — which event to listen for (e.g. `presentation.opened`)
* **Channel** — how to deliver the notification
* **Channel config** — channel-specific settings (see below)
* **Template scope** (optional) — limit to events from a specific template, or leave blank for all templates

## Delivery channels

### Webhook

Sends an HTTP POST request to your URL with the event payload as JSON.

Channel config:

```json theme={null}
{
  "url": "https://your-server.com/webhooks/ai-presentations",
  "headers": {
    "Authorization": "Bearer your-secret-token"
  }
}
```

The webhook payload follows this structure:

```json theme={null}
{
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_type": "presentation.opened",
  "event_version": 1,
  "organization_id": "org-uuid",
  "occurred_at": "2026-01-15T10:30:00Z",
  "payload": {
    "presentationId": "pres-uuid",
    "deckId": "deck-uuid"
  }
}
```

Webhook deliveries that fail are retried with exponential backoff.

### Email

Sends a formatted email notification to one or more recipients.

Channel config:

```json theme={null}
{
  "toEmails": ["sales@yourcompany.com", "manager@yourcompany.com"]
}
```

You can add up to 20 email recipients per subscription.

### Console

Logs events to the server console. Useful for development and debugging.

```json theme={null}
{}
```

## Event log

AI Presentations keeps a log of all events for your organization. You can query the log to see what happened during a presentation, filter by event type, or look at events within a time range.

For `email.sent` events, each entry includes a top-level `recipientEmail` field. AI Presentations resolves it from the delivery attempt referenced by the payload's `deliveryId`; the stored payload itself never contains the address.

The event log supports filtering by:

* **Event type** — e.g. only `contract.signed` events
* **Aggregate type** — e.g. only presentation-level events
* **Aggregate ID** — events for a specific presentation
* **Time range** — `since` and `until` parameters
* **Pagination** — `limit` (max 100) and `offset`

### Event retention

Event log entries keep their full payloads for 90 days. After that, AI Presentations archives each entry and reduces its payload to identifier and classification fields — for example `presentationId`, `slideIndex`, `durationMs`, the `skipped` flag, `ctaType`, and delivery status. The entry itself stays in the log, so activity timelines keep their full history, and skip-rate and dwell-time metrics stay consistent for the life of the presentation.

Archived entries carry an `archivedAt` timestamp. For `question.asked` and `question.escalated`, the question and answer text is removed when the entry is archived; the classification fields (such as `answerSource` and `escalationReason`) and the deck and slide identity fields (`deckId`, `deckSlideId`, `slideTitle`, `slideIndex`) remain, so per-slide question metrics stay consistent past the 90-day window. Archived entries are deleted from the log entirely after two years.

AI Presentations keeps an internal archive copy of each entry's original payload for 9 months after archiving, then deletes it. No raw question or answer text remains in any store AI Presentations controls more than 12 months after the event occurred.

### Q\&A answer retention

When a homeowner asks a question during a presentation, AI Presentations caches the generated answer, along with its spoken audio, so similar questions on the same deck can be answered instantly. Cached answers follow the same retention limits as the event log. AI Presentations deletes a cached answer after 90 days without use, or 12 months after it was created, whichever comes first. The spoken audio is deleted with it. Expiry has no visible effect during presentations: the next matching question generates a fresh answer.

## Managing subscriptions

You can enable, disable, update, or delete subscriptions at any time. Disabling a subscription (`isActive: false`) stops deliveries without losing the configuration.

## Automated email delivery

AI Presentations emails the presentation link directly to homeowners when a presentation is created from a journey ingest, and on demand when you call the send endpoint. Each deck has its own delivery configuration with a custom sender name, reply-to address, subject line, body template, and CTA label. Every send is recorded as a delivery attempt regardless of outcome.

A send is `skipped` only when the homeowner record has no email address (manual sends with override recipients bypass that check). There is no separate enable toggle — if a deck is connected to a journey or you call the send endpoint, the email goes out.

Configure per-deck delivery settings from the **Delivery** tab in the deck editor, or via the API:

* `GET /api/decks/{deckId}/delivery` — read the current delivery config
* `PUT /api/decks/{deckId}/delivery` — set sender, subject, body, and CTA label

Trigger a send (or resend) for a specific presentation:

* `POST /api/presentations/{presentationId}/send` — send the homeowner email using the deck's delivery config. Optionally pass `{ "emails": ["..."] }` to override the default recipients. Returns a `delivery_id` and a status of `sent`, `skipped`, or `failed`. If the presentation's own generation failed, the send is refused with a `409` and code `PRESENTATION_FAILED` — there's nothing usable to deliver.

Each send emits an `email.sent` event. When SendGrid reports the recipient has opened the message, AI Presentations records the open and emits an `email.opened` event — subscribe to it from your existing notification subscriptions to track opens in real time. `email.opened` fires once per delivery, on the first open SendGrid reports. Opens triggered by mail-client privacy features (such as Apple Mail Privacy Protection prefetching images) count as opens.

AI Presentations also records every delivery outcome SendGrid reports, including delivered, deferred, bounced, dropped, spam report, unsubscribe, and click events. Failures that need your attention emit their own events: `email.bounced`, `email.dropped`, `email.complained`, and `email.unsubscribed`. Subscribe to them to find out when a presentation email never reached the homeowner or when a homeowner opts out. There is no `email.delivered` event; `email.sent` already covers the send itself.

## API reference

Key notification endpoints:

* `GET/POST /api/notifications/subscriptions` — list or create subscriptions
* `GET/PUT/DELETE /api/notifications/subscriptions/{subscriptionId}` — manage a specific subscription
* `GET /api/notifications/events` — query the event log with filters and pagination
* `GET/PUT /api/decks/{deckId}/delivery` — manage per-deck email delivery settings
* `POST /api/presentations/{presentationId}/send` — send or resend the homeowner email

See the **API Reference** tab for full request and response details.
