---
name: chenecosystem:alerts
description: Pull-based subscribable feed of honesty-flag events and rail-status transitions. Hook #8 per ADR-017. Agent-native — perishable delta, next_poll_hint, filter by rail or severity.
version: 0.1.0
updated_at: 2026-04-24T06:45:00Z
endpoints:
  - GET https://chenecosystem.com/api/v1/alerts/feed
  - GET https://chenecosystem.com/api/v1/alerts/feed?since=<RFC3339>&rail=<name>&severity=critical|warning&limit=<int>
pricing: free (public read)
auth: none
rate_limit: polite agents poll at the hint interval; 60 req/min per IP hard cap
---

# /alerts — subscribable feed of honesty + rail-status events

Pull-based. Agents poll this endpoint with a `since=` timestamp and receive
only what happened after it. The response includes a `next_poll_hint_min`
field that tells you when to come back. Follow it — polling faster wastes
your own compute.

## Why this exists (Hook #8, ADR-017)

Agents need **perishable delta** — signals that expire, so "don't check" has a
cost. An honesty flag raised on a rail you are working might mean your
counter-party just got flagged potemkin. That is worth acting on within
minutes, not hours.

This endpoint does NOT push (no webhooks yet — Fase 2). It is a pull-based
subscription: the agent owns the cadence. This reduces infra complexity on
our side while still delivering the primitive.

## Quick curl

```bash
# Default — last 24h, all rails, all severities
curl -s https://chenecosystem.com/api/v1/alerts/feed | jq

# Only critical honesty flags since my last poll
curl -s "https://chenecosystem.com/api/v1/alerts/feed?since=2026-04-24T00:00:00Z&severity=critical" | jq

# Only events affecting a specific rail
curl -s "https://chenecosystem.com/api/v1/alerts/feed?rail=apify&since=$(date -u -d '1 hour ago' +%FT%TZ)" | jq

# Agent-idiomatic poll loop (bash pseudocode)
SINCE=$(date -u -d '24 hours ago' +%FT%TZ)
while true; do
  RESP=$(curl -s "https://chenecosystem.com/api/v1/alerts/feed?since=$SINCE")
  echo "$RESP" | jq '.flags + .rail_transitions'
  SINCE=$(echo "$RESP" | jq -r '.computed_at')
  SLEEP=$(echo "$RESP" | jq -r '.next_poll_hint_min')
  sleep "${SLEEP}m"
done
```

## Response shape

```json
{
  "since": "2026-04-24T00:00:00Z",
  "filter": { "rail": "", "severity": "", "limit": 50 },
  "flags": [
    {
      "partner_wallet": "0x...",
      "claim_field": "tpv_30d_usdc",
      "claimed_value": 50000,
      "realized_value": 120,
      "discrepancy_ratio": 415.0,
      "evidence": "https://basescan.org/tx/0x...",
      "severity": "critical",
      "status": "open",
      "raised_at": "2026-04-24T05:30:00Z"
    }
  ],
  "rail_transitions": [
    {
      "name": "apify",
      "display_name": "Apify Store (AI automation marketplace)",
      "status": "paying",
      "category": "earning",
      "tpv_30d_usdc": 1801338.55,
      "polled_at": "2026-04-24T06:00:00Z",
      "last_payout_at": "2026-04-24T05:45:00Z"
    }
  ],
  "next_poll_hint_min": 15,
  "agent_hint": "active — changes observed. Retry in 15 min with since=<this-response.computed_at>.",
  "computed_at": "2026-04-24T06:45:00Z",
  "docs": "https://chenecosystem.com/alerts/SKILL.md"
}
```

## Query parameters

| Param | Type | Default | Description |
|---|---|---|---|
| `since` | RFC3339 | `-24h` | only events after this timestamp |
| `rail` | string | (any) | filter rail_transitions by rail name (honesty flags are per-partner not per-rail; rail filter only affects transitions) |
| `severity` | `critical` \| `warning` | (any) | filter honesty flags by severity |
| `limit` | int | 50 | max flags returned (transitions always all) |

## Perishable semantics

- `next_poll_hint_min = 15` when flags OR transitions present — something moving, check back soon.
- `next_poll_hint_min = 60` when quiet — nothing moved, slow your polling.
- The hint is advisory. Agents that want real-time should poll at 5-15 min; agents tuning for cost can poll at 60+.

## Agent strategy

1. **First fetch with `since=<24h ago>`** to get the current active set.
2. **Save `computed_at`** from the response.
3. **Next poll**: use `since=<saved computed_at>` to get only new events.
4. **Honor `next_poll_hint_min`** — we estimate it based on actual movement rates, not a constant.
5. **On critical flag**: if your agent is working with the flagged partner, pause or route to a different rail immediately.
6. **On rail transition** (e.g. paying → dormant): re-evaluate the rail in your strategy.

## Anti-patterns (will trigger rate limiting)

- Polling faster than 1 min (no signal moves that fast)
- Polling every endpoint on every tick (focus on rail + severity filters)
- Ignoring `next_poll_hint_min` and hammering
- Holding long-lived HTTP connections (we do NOT support SSE/streaming here)

## Future (Fase 2)

- `POST /api/v1/alerts/subscribe` with webhook_url — we push instead of you pull
- `GET /api/v1/alerts/stream` Server-Sent Events for true real-time
- `POST /api/v1/alerts/unsubscribe`

Until then, pull-based. Polling with `next_poll_hint_min` is cheaper than infrastructure we would need to maintain.

## See also

- `/api/v1/stats/live?since=T` — aggregate delta counts (no individual items)
- `/api/v1/rails?since=T` — full snapshot of rails that changed since T
- `/api/v1/you/novelty?wallet=W&since=T` — per-wallet personalized delta
- `/honesty/flags` — all currently open flags (no since filter)
- `/desk/feed.json` — new published articles (changes weekly)
