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

# Best Time to Send Notification

## Solution Background and Business Value

Selecting the optimal notification send time for each client drives up client interactions, and ultimately conversions, while reducing notification fatigue.

**Example industries:**

* **E-commerce:** Send product restock alerts when clients are most active.
* **SaaS:** Deliver feature-announcement emails when each client typically checks their inbox.
* **Media & Publishing:** Time newsletters to align with individual reading habits.

**Value to your business:**

* **Personalization at scale:** Tailor send times per client without manual rules.
* **Higher engagement:** Emails hit inboxes when clients are most likely to act.
* **Lower churn & complaints:** Avoid “wrong-time” sends that irritate clients.

## Data Requirements and Schema

Kumo AI processes relational data as interconnected tables using Graph Transformers. This approach allows the model to learn from previous client interactions with historical notifications  without feature engineering.

#### **Core Tables**

1. **Client Table**
   * Store all client records. 
   * **Key attributes:**
     * `client_id`: Unique identifier for each client.
     * `email`: client email address.
     * **Optional:** Location, name, other client meta-data.
2. **Campaign Table**
   * Store all marketing campaign records.
   * **Key attributes:**
     * `campaign_id`: Unique identifier for each campaign.
     * **Optional:** campaign name, content, other campaign details.
3. **Send Event Table**
   * Store all send email event records.
   * **Key attributes:**
     * `send_id`: Unique identifier for each send action
     * `campaign_id`: links send events to a unique marketing campaign.
     * `client_id`: links send events to a client.
     * `send_timestamp`: when email was sent.
4. **Open Event Table**
   * Captures every time a sent email is opened, tied back to the original send.
   * **Key attributes:**
     * `open_id`: Unique identifier per open event.
     * `send_id`: references email send event.
     * `open_timestamp`: when the client opened the email.
5. **Click Event Table**
   * Records each click on any tracked link inside an email, linked to the send action.
   * **Key attributes:**
     * `click_id`: Unique identifier per click event.
     * `send_id`: references email send event.
     * `click_timestamp`: when the client clicked the link in the email.

**Entity Relationship Diagram (ERD)**

```mermaid theme={null}
erDiagram
    CLIENT {
        INT    client_id PK
        VARCHAR email
        VARCHAR location
        VARCHAR name
    }
    CAMPAIGN {
        INT    campaign_id PK
        VARCHAR name
        VARCHAR content_type
    }
    SEND_EVENT {
        INT       send_id PK
        INT       client_id FK
        INT       campaign_id FK
        TIMESTAMP send_timestamp
    }
    OPEN_EVENT {
        INT       open_id PK
        INT       send_id FK
        TIMESTAMP open_timestamp
    }
    CLICK_EVENT {
        INT       click_id PK
        INT       send_id FK
        TIMESTAMP click_timestamp
    }

    CLIENT           ||--o{ SEND_EVENT   : "has send events"
    CAMPAIGN         ||--o{ SEND_EVENT   : "has send events"
    SEND_EVENT ||--o{ OPEN_EVENT   : "records opens"
    SEND_EVENT ||--o{ CLICK_EVENT  : "records clicks"
```

## Predictive Query for Best-Time to Send

Predict first hour that would maximize probability of client opening campaign email based on past client-marketing campaign interactions.

```pql theme={null}
PREDICT FIRST(open_event.hour,0,1,days)
FOR EACH client.client_id
ASSUMING COUNT(open_event.*,0,1,days) > 0
```

## Deployment Strategy

**Batch Prediction for Campaign Planning (overnight/daily):**

* For each upcoming campaign, generate one “open probability” score per candidate send-open per client.
* Pick the top-scoring hour and schedule sends via your ESP.

## Building models in Kumo SDK

**1. Initialize the Kumo SDK**

```python theme={null}
import kumoai as kumo

kumo.init(url="https://<customer_id>.kumoai.cloud/api", api_key=API_KEY)
```

**2. Connect data**

```python theme={null}
connector = kumo.S3Connector("s3://your-dataset-location/")
```

**3. Select tables**

```python theme={null}
clients = kumo.Table.from_source_table(
	source_table=connector.table("client"), 
	primary_key="client_id").infer_metadata()

campaigns = kumo.Table.from_source_table(
	source_table=connector.table("campaign"), 
	primary_key="campaign_id").infer_metadata()

sends   = kumo.Table.from_source_table(
	source_table=connector.table("send_event"), 
	time_column="send_timestamp").infer_metadata()

opens   = kumo.Table.from_source_table(
	source_table=connector.table("open_event"), 
	time_column="open_timestamp").infer_metadata()

clicks  = kumo.Table.from_source_table(
	source_table=connector.table("click_event"), 
	time_column="click_timestamp").infer_metadata()
```

**4. Create graph schema**

```python theme={null}
graph = kumo.Graph(
    tables={
        "client": clients,
        "campaign": campaigns,
        "send_event": sends,
        "open_event": opens,
        "click_event": clicks,
    },
    edges=[
        dict(src_table='send_event', fkey='client_id', dst_table='client'),
        dict(src_table='send_event', fkey='campaign_id', dst_table='campaign'),
        dict(src_table='open_event', fkey='send_id', dst_table='send_event'),
        dict(src_table='click_event', fkey='send_id', dst_table='send_event'),
    ],
)
graph.validate(verbose=True)
```

**5. Train the model**

```python theme={null}
pquery = kumo.PredictiveQuery(
    graph=graph,
    query="""
		PREDICT FIRST(open_event.hour,0,1,days)
		FOR EACH client.client_id
		ASSUMING COUNT(open_event.*,0,1,days) > 0
		"""
)

pquery.validate(verbose=True)

model_plan = pquery.suggest_model_plan()
trainer = kumo.Trainer(model_plan)
training_job = trainer.fit(
    graph=graph,
    train_table=pquery.generate_training_table(non_blocking=True),
    non_blocking=False,
)
print(f"Training metrics: {training_job.metrics()}")
```
