Skip to content
miblo

SDK

Alert-only Miblo apps: types, sending, testing and status

A Miblo app with no screen that only alerts: screen: false, the alert types, alerts.send, test, status, a key against repeats, quiet hours and every error.

What an alert-only app is

Most apps don't need a screen: they only need to say when something happens. A goal, a site down, a broken build, a parcel out for delivery. An alert-only app is just that: it draws no card, never takes a turn among the app screens and is never pinned. It declares the kinds of alert it sends, and the person turns each one on or off in the Miblo app (Apps tab).

The alert shows over whatever the Miblo shows for a few seconds, then the screen comes back. Miblo's own alerts ("needs you", a finished session) always come first: yours waits for them.

In ten lines

import { MibloStatus } from '@miblo/status';

const miblo = new MibloStatus();
const alerts = miblo.alerts({ id: 'placar', tool: 'Placar' });
await alerts.declare({ name: 'Placar', types: [
  { id: 'gol', label: { pt: 'Gol', en: 'Goal' }, level: 'important' },
  { id: 'fim', label: { pt: 'Fim de jogo', en: 'Full time' }, level: 'info' },
] });
await alerts.send({ type: 'gol', title: 'GOAL: Flamengo!', text: 'Flamengo 2 x 1', key: 'match-123-goal-2' });

declare() registers the app as alert-only (screen: false). No screen(), card or preview needed.

The alert types

Each type is { id, label, level, default, cooldownMin }, 1 to 8 per app:

  • id: lowercase letters, digits and -, up to 16 (never on, off, later, test or history). It is each alert's type.

  • label: the name the person sees next to the switch, up to 24 characters, text or { pt, en }.

  • level: info (the notice) or important (the screen flashes first, like Miblo's own alerts). It is that type's maximum: an important alert of an info type shows as info.

  • default: on (true, the default) or off until the person turns it on.

  • cooldownMin: optional, 0 to 1440. After an alert of this type, the next ones wait that many minutes.

send()

alerts.send({ type, title, text, level, seconds, key, dedupeMin, whenQuiet }) (also alerts.alert()):

  • title up to 24 characters (required), text up to 40, seconds 1 to 15 (default 8).

  • key: a key for the event. The same key within dedupeMin minutes (default 10) is one alert: the answer is { ok: true, duplicate: true }. Use it so the same goal is never told twice when your program reads the score again.

  • whenQuiet: what to do during the person's quiet hours. 'drop' (default) drops it; 'later' keeps it (up to 3 per app) and delivers it when the quiet hours end. The client's default comes from miblo.alerts({ id, tool, whenQuiet }).

For fire-and-forget programs, trySend() (try_send() in Python) never throws: it returns { ok, code, retryAfter }.

test() and status()

await alerts.test();                 // "Test · Placar" on the Miblo, even before permission
const st = await alerts.status();    // permission, types on, quiet hours, how many left today
  • test({ type, title, text }) shows an alert whose title starts with "Test ·". It works before the person allows the alerts (it is there to see how it looks), but keeps a type turned off, quiet hours, the 2-minute gap and 10 tests a day.

  • status() returns { permission, types: [{ id, label, level, on }], quiet: { on, from, to, active, endsInS }, limit: { left, nextInS }, queued, last }.

Errors

Every error is a MibloError with a code:

  • alerts_off: the person has not allowed the app's alerts yet (asked: true: the first alert asked them in the Miblo app) or turned them off (asked: false).

  • type_off: the person turned that type off (type).

  • quiet: it is quiet hours; retryAfter says in how many seconds they end.

  • rate_limited: retryAfter in seconds; reason says whether it was the type's cooldown or the app's limit (1 every 2 minutes, 20 a day).

  • bad_request with field: a title too long, a type the app did not declare.

As a store app

An alert-only community app is a zip with main.js and manifest.json, like any other (Publish to the store). In the manifest, screen: false and the types in alerts:

JSON
{
  "id": "placar",
  "name": { "pt": "Placar", "en": "Score" },
  "promise": { "pt": "Avisa os gols do seu time", "en": "Tells you your team's goals" },
  "screen": false,
  "alerts": [
    { "id": "gol", "label": { "pt": "Gol", "en": "Goal" }, "level": "important", "default": true },
    { "id": "fim", "label": { "pt": "Fim de jogo", "en": "Full time" }, "level": "info", "default": true }
  ],
  "settings": [],
  "network": ["site.api.espn.com"],
  "intervalMs": 60000
}

main.js exports run(ctx) and returns no card: it sends alerts with ctx.alerts.send(...) or returns them in { alerts: [...] }:

JavaScript
export async function run(ctx) {
  const match = await ctx.fetchJson('https://site.api.espn.com/...');
  if (match.goals > (ctx.state.goals ?? 0)) {
    ctx.state.goals = match.goals;
    return { alerts: [{ type: 'gol', title: 'GOAL!', text: match.score, key: `${match.id}-${match.goals}` }] };
  }
  return {};
}

In the submission wizard, answer "It only sends alerts" to the first question: instead of the card, it asks for the alert types. Screenshots are optional (a photo of an alert on the Miblo helps).

What the person controls

In the Miblo app (Apps tab), an alert-only app shows with an "Alerts" badge and no screen preview:

  • Allow alerts from the app (or not), and one switch per type.

  • Test alert, to see how it looks.

  • The history of the last alerts: when, the type, the title, and whether it was delivered or why not.

  • The Miblo's quiet hours (say, 22:00 to 08:00), which hold every app's alerts.

In a terminal: miblo apps alerts <id> on|off, miblo apps alerts <id> <type> on|off, miblo apps alerts <id> test, miblo apps alerts <id> history and miblo apps quiet.