Alert Triggers

A trigger is a named DSL query plus a lastSeenAt bookmark. On each page load the app re-evaluates every trigger and flags matches that landed since you last looked.

Last updated · 2026-04-17

Overview

Triggers are for intel workers who want to leave the browser and come back to a prioritized list. Instead of polling the API on your own schedule, you declare what matters and let the nav badge surface new matches.

The syntax is the same DSL used on /dashboard/explore and /api/query — so a query you validated in Explore can be pasted directly into a trigger.

Creating a trigger

Three paths:

  1. In the Explore DSL editor, click + Alert trigger. It deep-links to /dashboard/alerts?dsl=… with the query prefilled.
  2. Navigate to /dashboard/alerts directly and fill out the New trigger form.
  3. Programmatically via localStorage — see Storage below.

Evaluation model

On page load, the app collects all triggers from localStorage and issues a single POST /api/alerts/check call with the full batch. The server evaluates each DSL against the current record set and returns match counts.

Each trigger returns:

  • match_count — total records currently matching
  • unread_count — records whose last_seenis newer than the trigger's lastSeenAt
  • last_match_at — most recent last_seen among matches
  • example_record_id— ID of the most recent match (for the “View latest” link)

Unread semantics

“Unread” means new since you last looked, not new since the record was created. When you click Mark read, the trigger's lastSeenAt is set to now — subsequent evaluations only count records that appear or update after that timestamp.

Visiting /dashboard/alerts does not automatically mark everything read — the page shows the unread badge so you can triage. Click into individual triggers, or click Mark all read to clear the nav badge.

Storage

Triggers live in localStorage under the key boarnet.alerts.triggers as a JSON array.

localStorage shape
[
  {
    "id": "trg_m4x82_abc123",
    "name": "Alpha pivots in Russia",
    "dsl": "tag:credential-stuffing AND country:RU",
    "createdAt": 1776400000000,
    "lastSeenAt": 1776480000000
  }
]

API: POST /api/alerts/check

The nav badge and /alerts page both hit this endpoint on mount. It accepts a list of triggers and returns per-trigger evaluation results.

request
POST /api/alerts/check
Content-Type: application/json
Authorization: Bearer <optional>

{
  "triggers": [
    { "id": "trg_1", "dsl": "tag:credential-stuffing", "lastSeenAt": 1776480000000 },
    { "id": "trg_2", "dsl": "ja4:~t13d",               "lastSeenAt": 0 }
  ]
}
response
{
  "tier": "anonymous",
  "results": [
    {
      "id": "trg_1",
      "ok": true,
      "match_count": 8,
      "unread_count": 2,
      "last_match_at": "2026-04-18T01:59:00.000Z",
      "example_record_id": "thr_demo01"
    },
    {
      "id": "trg_2",
      "ok": false,
      "error": "These fields require a Pro key: ja4",
      "match_count": 0,
      "unread_count": 0,
      "last_match_at": null
    }
  ]
}

Limits

  • No hard cap on triggers; realistic ceiling is ~50 per browser
  • Each POST to /api/alerts/check costs 1 call against your rate limit
  • Triggers that reference paywalled fields will evaluate with ok: false on anonymous keys