Skip to main content

Overview

Notification tasks are the core of hans.ai. They define:
  • What to monitor (URL)
  • How to evaluate changes (evaluation function)
  • When to check (schedule)
  • What to do when conditions are met (actions)

Creating a Notification Task

Basic Structure

{
  "name": "Product Price Monitor",
  "description": "Monitor price changes for a specific product",
  "url": "https://example.com/product/123",
  "evaluationFunction": "// JavaScript code to evaluate the page",
  "zodSchema": "// Optional Zod schema for data extraction",
  "actions": [
    // Array of action configurations
  ],
  "schedule": {
    "cron": "*/30 * * * *" // Check every 30 minutes
  }
}

Evaluation Functions

Evaluation functions are JavaScript code that runs in the context of the monitored page. They determine whether to trigger actions.

Simple Boolean Evaluation

// Returns true when price drops below $100
const priceElement = document.querySelector('.price');
const price = parseFloat(priceElement.textContent.replace('$', ''));
return price < 100;

Data Extraction with Schema

When you provide a Zod schema, the evaluation function should return data matching that schema:
// Extract structured data
const product = {
  name: document.querySelector('h1').textContent,
  price: parseFloat(document.querySelector('.price').textContent.replace('$', '')),
  inStock: document.querySelector('.availability').textContent.includes('In Stock'),
  rating: parseFloat(document.querySelector('.rating').textContent),
};

return product;
With corresponding Zod schema:
z.object({
  name: z.string(),
  price: z.number(),
  inStock: z.boolean(),
  rating: z.number().min(0).max(5),
});

Stagehand Scripts

For complex interactions requiring browser automation, use Stagehand scripts:
{
  "useStagehandScript": true,
  "stagehandScript": `
    await page.goto(url);
    await page.click('.load-more');
    await page.waitForSelector('.results');

    const results = await page.evaluate(() => {
      return Array.from(document.querySelectorAll('.item')).map(item => ({
        title: item.querySelector('.title').textContent,
        price: item.querySelector('.price').textContent
      }));
    });

    return results.length > 10;
  `
}

Task Execution

Manual Execution

Trigger a task immediately via API:
POST /api/notifications/{id}/execute

Scheduled Execution

Tasks run automatically based on their cron schedule:
{
  "schedule": {
    "cron": "0 9 * * *"  // Daily at 9 AM
  }
}
Common cron patterns:
  • */5 * * * * - Every 5 minutes
  • 0 * * * * - Every hour
  • 0 0 * * * - Daily at midnight
  • 0 0 * * 1 - Weekly on Monday
  • 0 0 1 * * - Monthly on the 1st

Execution Results

Each execution produces a result:
{
  "id": "result-123",
  "taskId": "task-456",
  "executedAt": "2024-01-01T12:00:00Z",
  "success": true,
  "data": {
    // Extracted data if schema provided
  },
  "error": null,
  "actionsTriggered": 2
}

Best Practices

Reliable Selectors

Use stable CSS selectors or data attributes that won’t break with minor UI changes

Error Handling

Include try-catch blocks in evaluation functions to handle missing elements gracefully

Efficient Schedules

Balance monitoring frequency with API limits and resource usage

Test First

Test evaluation functions manually before scheduling

Advanced Features

Conditional Actions

Trigger different actions based on evaluation results:
const price = parseFloat(document.querySelector('.price').textContent.replace('$', ''));

if (price < 50) {
  return { level: 'urgent', price };
} else if (price < 100) {
  return { level: 'normal', price };
}

return false; // Don't trigger actions

Multi-Page Monitoring

Monitor multiple pages with a single task using Stagehand:
const urls = ['https://example.com/product/1', 'https://example.com/product/2'];

const results = [];
for (const url of urls) {
  await page.goto(url);
  const price = await page.evaluate(() => {
    return document.querySelector('.price').textContent;
  });
  results.push({ url, price });
}

return results;

Troubleshooting

Ensure your function explicitly returns a value. Use return false instead of no return statement.
The page might be dynamically loaded. Use Stagehand scripts with waitForSelector for dynamic content.
Check that the cron expression is valid and the task is active. Verify system time and timezone settings.