A new module, modules/overflow-tap, that does what Overflow's "ProPresenter Integration" helper app does, inside ProdDash — speaking the same Overflow API the helper speaks — so the helper on AB-ProPresenter can be retired once this is configured there.
Reads the live slide's notes from ProPresenter: the presentation layer (GET /v1/status/slide) and the announcement layer (GET /v1/announcement/slide_index + /active), once a second each.
When a layer's set of keywords changes to a non-empty set, tells Overflow, which points those keywords' Tap groups at their destinations. A slide without a keyword changes nothing; the last destination stays. Matching is the helper's rule: a whole word of the notes, any case, punctuation around it ignored (give fires on "Give!" and "#give", not on "forgiveness").
One tile: the keyword Tap is on, its destination and the groups moved, a chip saying whether the slide on screen — or the announcement — carries a keyword, and the last four switches with their times (✓ / ✕). No status words in the tile: how a switch went is the corner dot's job. A switch Overflow refuses is retried three times, 10 s apart, with the dot amber meanwhile; only when those are spent does the dot go red (hover for the reason) and the alert fire, if switched on in admin. The retry budget counts refusals in a row across keywords, so a dead Overflow turns the dot red even while the service keeps bringing new keywords; the alert is raised once per outage, and a success clears the streak. Four tile switches: Destination, Groups, Slide match, History. Title bar: one icon, Refresh keywords.
Admin config, in three groups: ProPresenter (endpoint + API password), Overflow (a Dashboard login: email + password, plus a Nonprofit ID only for an @overflow.co staff login), Alerts (toggle + channel). The keyword → destination → Tap groups mapping stays in the Overflow Dashboard (Integrations › ProPresenter › Keyword Mappings) and is read from there on start, every five minutes, on the tile's refresh button, and from every trigger's answer.
Health line (admin): Following host:port · Overflow: booth@church.org, 4 keywords. The tile's own status line carries no email.
The Overflow API, and where it came from
Overflow's public API documents only reads for Tap. The first cut of this PR guessed a write route; this version instead makes exactly the calls the helper app makes (Overflow ProPresenter Integration 1.2.3, bundle read 2026-10-11 with the user's approval), against the Dashboard's own backend at server.overflow.co:
Call
What for
POST /nonprofit-dashboard/authentication/login/jwt{ email, password }
a Dashboard sign-in → JWT (payload: nonprofitId, permissions with subject TAP, exp)
GET /nonprofit-dashboard/propresenter/nonprofits/{id}/keywords
POST …/nonprofits/{id}/keywords?syncNonprofit=true{ keywords }
the switch
GET …/propresenter/nonprofits, POST …/nonprofits/{id}/select
staff logins only
All of it is in one file, overflow.js, with the provenance at the top. Session handling: the token is cached across re-inits for the same login (an admin saving config five times must not become five sign-ins — Overflow answers 429 "too many attempts"); a 401 signs in again once and retries; a refused sign-in is not retried within 20 s. Google sign-in has no password, so the account needs one set — the admin help says so, as Overflow's article does.
Testing
node modules/overflow-tap/keywords.selftest.js — 8 cases pinning the matching rule.
node tools/overflow-tap-mock.js — a ProPresenter cycling through keyworded slides on :1602 (announcement layer included; --no-announcements for an older one) plus the Overflow Dashboard API on :24720: any email/password signs in; --fail-switch refuses every switch, --expire=20 makes sessions expire, --login=<email>:<password> accepts one login only. Run ProdDash with OVERFLOW_TAP_API_BASE=http://127.0.0.1:24720.
Against the mock: sign-in, keyword load, triggers on both layers (connect + events when both change in one tick), token expiry → sign-in again with switching uninterrupted, --fail-switch → dot amber through three refusals, red on the fourth with one alert Tap not switched: … on channel 1, later refusals red without re-alerting, green again on the first success; wrong login → the reason on the dot's hover and in the health line with three sign-in attempts in 45 s.
Resilience checklist (from the first cut, same tile code): upstream killed and restored, ProdDash restarted with the tile open, two tiles with different settings (a joining tile backfills from /state), tile removed with a clean console, admin config change mid-run, page reload.
Not yet exercised: a real Overflow account. That needs the Dashboard login entered on AB; the helper app there should stay until the module is seen switching.
Shell untouched. Requires ProdDash ≥ 1.11.0 (alerts). README and CLAUDE.md carry the module and the mock.
## What it is
A new module, `modules/overflow-tap`, that does what Overflow's "ProPresenter Integration" helper app does, inside ProdDash — speaking the same Overflow API the helper speaks — so the helper on AB-ProPresenter can be retired once this is configured there.
- Reads the live slide's notes from ProPresenter: the presentation layer (`GET /v1/status/slide`) and the announcement layer (`GET /v1/announcement/slide_index` + `/active`), once a second each.
- When a layer's set of keywords changes to a non-empty set, tells Overflow, which points those keywords' Tap groups at their destinations. A slide without a keyword changes nothing; the last destination stays. Matching is the helper's rule: a whole word of the notes, any case, punctuation around it ignored (`give` fires on "Give!" and "#give", not on "forgiveness").
- One tile: the keyword Tap is on, its destination and the groups moved, a chip saying whether the slide on screen — or the announcement — carries a keyword, and the last four switches with their times (✓ / ✕). No status words in the tile: **how a switch went is the corner dot's job.** A switch Overflow refuses is retried three times, 10 s apart, with the dot amber meanwhile; only when those are spent does the dot go red (hover for the reason) and the alert fire, if switched on in admin. The retry budget counts refusals in a row across keywords, so a dead Overflow turns the dot red even while the service keeps bringing new keywords; the alert is raised once per outage, and a success clears the streak. Four tile switches: Destination, Groups, Slide match, History. Title bar: one icon, Refresh keywords.
- Admin config, in three groups: **ProPresenter** (endpoint + API password), **Overflow** (a Dashboard login: email + password, plus a Nonprofit ID only for an @overflow.co staff login), **Alerts** (toggle + channel). **The keyword → destination → Tap groups mapping stays in the Overflow Dashboard** (Integrations › ProPresenter › Keyword Mappings) and is read from there on start, every five minutes, on the tile's refresh button, and from every trigger's answer.
- Health line (admin): `Following host:port · Overflow: booth@church.org, 4 keywords`. The tile's own status line carries no email.
## The Overflow API, and where it came from
Overflow's public API documents only reads for Tap. The first cut of this PR guessed a write route; this version instead makes exactly the calls the helper app makes (Overflow ProPresenter Integration 1.2.3, bundle read 2026-10-11 with the user's approval), against the Dashboard's own backend at `server.overflow.co`:
| Call | What for |
| --- | --- |
| `POST /nonprofit-dashboard/authentication/login/jwt` `{ email, password }` | a Dashboard sign-in → JWT (payload: nonprofitId, permissions with subject TAP, exp) |
| `GET /nonprofit-dashboard/propresenter/nonprofits/{id}/keywords` | the Keyword Mappings: `[{ keyword, destinationName, tapGroups: [{ id, tapGroupName }] }]` |
| `POST …/nonprofits/{id}/keywords?syncNonprofit=true` `{ keywords }` | the switch |
| `GET …/propresenter/nonprofits`, `POST …/nonprofits/{id}/select` | staff logins only |
All of it is in one file, `overflow.js`, with the provenance at the top. Session handling: the token is cached across re-inits for the same login (an admin saving config five times must not become five sign-ins — Overflow answers 429 "too many attempts"); a 401 signs in again once and retries; a refused sign-in is not retried within 20 s. Google sign-in has no password, so the account needs one set — the admin help says so, as Overflow's article does.
## Testing
- `node modules/overflow-tap/keywords.selftest.js` — 8 cases pinning the matching rule.
- `node tools/overflow-tap-mock.js` — a ProPresenter cycling through keyworded slides on `:1602` (announcement layer included; `--no-announcements` for an older one) plus the Overflow Dashboard API on `:24720`: any email/password signs in; `--fail-switch` refuses every switch, `--expire=20` makes sessions expire, `--login=<email>:<password>` accepts one login only. Run ProdDash with `OVERFLOW_TAP_API_BASE=http://127.0.0.1:24720`.
- Against the mock: sign-in, keyword load, triggers on both layers (`connect + events` when both change in one tick), token expiry → sign-in again with switching uninterrupted, `--fail-switch` → dot amber through three refusals, red on the fourth with one alert `Tap not switched: …` on channel 1, later refusals red without re-alerting, green again on the first success; wrong login → the reason on the dot's hover and in the health line with three sign-in attempts in 45 s.
- Resilience checklist (from the first cut, same tile code): upstream killed and restored, ProdDash restarted with the tile open, two tiles with different settings (a joining tile backfills from `/state`), tile removed with a clean console, admin config change mid-run, page reload.
Not yet exercised: a real Overflow account. That needs the Dashboard login entered on AB; the helper app there should stay until the module is seen switching.
Shell untouched. Requires ProdDash ≥ 1.11.0 (alerts). README and CLAUDE.md carry the module and the mock.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
A new module that does what Overflow's "ProPresenter Integration" helper app
does, inside ProdDash: the live slide's notes are read from ProPresenter
(GET /v1/status/slide, twice a second) and when they carry one of the admin
page's keywords, the chosen Overflow Tap groups are pointed at that keyword's
destination. One tile: the keyword Tap is on (and since when), its
destination and the groups moved, a chip saying whether the slide on screen
carries a keyword, and the last four switches. A failed switch turns the tile
red with the reason, retries three times, and can flash an alert channel.
Admin config: ProPresenter endpoint + password; Overflow client ID + API key
(x-client-id / x-api-key, the public API's two headers); Tap groups to switch
(multiselect read from the account over /groups); keywords as text, one per
line (`give = Give Online`, optionally `@ Lobby, Kids Wing`); match mode
(whole word / anywhere / own line); alerts switch + channel.
The one unverified piece: Overflow's public API documents only reads for Tap
(OpenAPI checked 2026-10-07), while Overflow's helper uses the Dashboard's
private API. The switch is sent as PATCH /api/v3/tap/groups/{id} with the new
destinationId — the natural shape for this API — and every switch is confirmed
by reading the group back, so a refusal shows on the tile instead of passing
silently. overflow.js is the one place to change if Overflow names a
different route.
Also: tools/overflow-tap-mock.js (a ProPresenter whose slide notes carry
keywords on :1602 plus the Overflow Tap API on :24720, --read-only to fail
every switch), keywords.selftest.js (14 cases), README + CLAUDE.md entries.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The first cut guessed a write route on Overflow's public API, which documents
only reads for Tap. With the helper app's bundle in hand (Overflow ProPresenter
Integration 1.2.3, read 2026-10-11) the module now makes exactly the calls the
helper makes, against the Dashboard's own backend:
POST /nonprofit-dashboard/authentication/login/jwt a Dashboard sign-in → JWT
GET /nonprofit-dashboard/propresenter/nonprofits/{id}/keywords
the Keyword Mappings
POST …/nonprofits/{id}/keywords?syncNonprofit=true { keywords } = the switch
So the keyword → destination → Tap groups mapping stays where Overflow keeps
it (Dashboard › Integrations › ProPresenter › Keyword Mappings) and is read
from there: on start, every five minutes, on the tile's refresh button, and
from every trigger's answer. Admin config shrinks to the ProPresenter endpoint,
the Overflow Dashboard login (plus a Nonprofit ID only for an @overflow.co
staff login) and the alert switch; the API-key, Tap-groups, keyword-text and
match-mode fields are gone.
Behaviour follows the helper: both the presentation layer (/v1/status/slide)
and the announcement layer (/v1/announcement/slide_index + /active) are
watched; when a layer's set of matched keywords changes to a non-empty set it
is sent; matching is per word, case-insensitive, punctuation around the word
ignored. The session token is cached across re-inits for the same login so an
admin saving config repeatedly cannot trip Overflow's "too many attempts";
a 401 signs in again once; a refused sign-in is not retried within 20 s.
tools/overflow-tap-mock.js now plays the Dashboard API (any email/password
signs in; --fail-switch, --expire=<s>, --login=<email>:<password>) and gives
the mock ProPresenter an announcement layer (--no-announcements for an older
one). Verified against it: sign-in, keyword load, triggers on both layers,
token expiry → re-login, 500 → NOT SWITCHED + alert + retries, wrong login →
the reason on the tile and in the health line, and recovery. keywords.selftest.js
pins the matching rule (8 cases).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The "TAP IS ON · since …" line (and its SWITCHING / NOT SWITCHED forms, and
the red error line under the destination) are gone from the tile. A tile
shows the keyword Tap is on, its destination, the groups moved, the slide
chip and the trail; status is the corner dot's job, as everywhere else in
ProdDash.
The dot now follows the retries honestly: a switch Overflow refuses is
retried three times, ten seconds apart, with the dot amber ("retrying (n of
4 attempts failed)") while the module is still fixing it itself; only when
the attempts are spent does the dot go red with the reason on hover, and the
alert fire if it is switched on. The budget counts refusals in a row across
keywords, not per keyword — a service keeps bringing new keywords, and each
one restarting a fresh budget meant a dead Overflow never turned the dot
red. The alert is raised once per outage, and a success clears the streak.
Verified against the mock with --fail-switch: amber through three refusals,
red on the fourth with one alert, later refusals stay red without
re-alerting, and green again on the first success once the mock is back.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What it is
A new module,
modules/overflow-tap, that does what Overflow's "ProPresenter Integration" helper app does, inside ProdDash — speaking the same Overflow API the helper speaks — so the helper on AB-ProPresenter can be retired once this is configured there.GET /v1/status/slide) and the announcement layer (GET /v1/announcement/slide_index+/active), once a second each.givefires on "Give!" and "#give", not on "forgiveness").Following host:port · Overflow: booth@church.org, 4 keywords. The tile's own status line carries no email.The Overflow API, and where it came from
Overflow's public API documents only reads for Tap. The first cut of this PR guessed a write route; this version instead makes exactly the calls the helper app makes (Overflow ProPresenter Integration 1.2.3, bundle read 2026-10-11 with the user's approval), against the Dashboard's own backend at
server.overflow.co:POST /nonprofit-dashboard/authentication/login/jwt{ email, password }GET /nonprofit-dashboard/propresenter/nonprofits/{id}/keywords[{ keyword, destinationName, tapGroups: [{ id, tapGroupName }] }]POST …/nonprofits/{id}/keywords?syncNonprofit=true{ keywords }GET …/propresenter/nonprofits,POST …/nonprofits/{id}/selectAll of it is in one file,
overflow.js, with the provenance at the top. Session handling: the token is cached across re-inits for the same login (an admin saving config five times must not become five sign-ins — Overflow answers 429 "too many attempts"); a 401 signs in again once and retries; a refused sign-in is not retried within 20 s. Google sign-in has no password, so the account needs one set — the admin help says so, as Overflow's article does.Testing
node modules/overflow-tap/keywords.selftest.js— 8 cases pinning the matching rule.node tools/overflow-tap-mock.js— a ProPresenter cycling through keyworded slides on:1602(announcement layer included;--no-announcementsfor an older one) plus the Overflow Dashboard API on:24720: any email/password signs in;--fail-switchrefuses every switch,--expire=20makes sessions expire,--login=<email>:<password>accepts one login only. Run ProdDash withOVERFLOW_TAP_API_BASE=http://127.0.0.1:24720.connect + eventswhen both change in one tick), token expiry → sign-in again with switching uninterrupted,--fail-switch→ dot amber through three refusals, red on the fourth with one alertTap not switched: …on channel 1, later refusals red without re-alerting, green again on the first success; wrong login → the reason on the dot's hover and in the health line with three sign-in attempts in 45 s./state), tile removed with a clean console, admin config change mid-run, page reload.Not yet exercised: a real Overflow account. That needs the Dashboard login entered on AB; the helper app there should stay until the module is seen switching.
Shell untouched. Requires ProdDash ≥ 1.11.0 (alerts). README and CLAUDE.md carry the module and the mock.
🤖 Generated with Claude Code
A new module that does what Overflow's "ProPresenter Integration" helper app does, inside ProdDash: the live slide's notes are read from ProPresenter (GET /v1/status/slide, twice a second) and when they carry one of the admin page's keywords, the chosen Overflow Tap groups are pointed at that keyword's destination. One tile: the keyword Tap is on (and since when), its destination and the groups moved, a chip saying whether the slide on screen carries a keyword, and the last four switches. A failed switch turns the tile red with the reason, retries three times, and can flash an alert channel. Admin config: ProPresenter endpoint + password; Overflow client ID + API key (x-client-id / x-api-key, the public API's two headers); Tap groups to switch (multiselect read from the account over /groups); keywords as text, one per line (`give = Give Online`, optionally `@ Lobby, Kids Wing`); match mode (whole word / anywhere / own line); alerts switch + channel. The one unverified piece: Overflow's public API documents only reads for Tap (OpenAPI checked 2026-10-07), while Overflow's helper uses the Dashboard's private API. The switch is sent as PATCH /api/v3/tap/groups/{id} with the new destinationId — the natural shape for this API — and every switch is confirmed by reading the group back, so a refusal shows on the tile instead of passing silently. overflow.js is the one place to change if Overflow names a different route. Also: tools/overflow-tap-mock.js (a ProPresenter whose slide notes carry keywords on :1602 plus the Overflow Tap API on :24720, --read-only to fail every switch), keywords.selftest.js (14 cases), README + CLAUDE.md entries. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>The first cut guessed a write route on Overflow's public API, which documents only reads for Tap. With the helper app's bundle in hand (Overflow ProPresenter Integration 1.2.3, read 2026-10-11) the module now makes exactly the calls the helper makes, against the Dashboard's own backend: POST /nonprofit-dashboard/authentication/login/jwt a Dashboard sign-in → JWT GET /nonprofit-dashboard/propresenter/nonprofits/{id}/keywords the Keyword Mappings POST …/nonprofits/{id}/keywords?syncNonprofit=true { keywords } = the switch So the keyword → destination → Tap groups mapping stays where Overflow keeps it (Dashboard › Integrations › ProPresenter › Keyword Mappings) and is read from there: on start, every five minutes, on the tile's refresh button, and from every trigger's answer. Admin config shrinks to the ProPresenter endpoint, the Overflow Dashboard login (plus a Nonprofit ID only for an @overflow.co staff login) and the alert switch; the API-key, Tap-groups, keyword-text and match-mode fields are gone. Behaviour follows the helper: both the presentation layer (/v1/status/slide) and the announcement layer (/v1/announcement/slide_index + /active) are watched; when a layer's set of matched keywords changes to a non-empty set it is sent; matching is per word, case-insensitive, punctuation around the word ignored. The session token is cached across re-inits for the same login so an admin saving config repeatedly cannot trip Overflow's "too many attempts"; a 401 signs in again once; a refused sign-in is not retried within 20 s. tools/overflow-tap-mock.js now plays the Dashboard API (any email/password signs in; --fail-switch, --expire=<s>, --login=<email>:<password>) and gives the mock ProPresenter an announcement layer (--no-announcements for an older one). Verified against it: sign-in, keyword load, triggers on both layers, token expiry → re-login, 500 → NOT SWITCHED + alert + retries, wrong login → the reason on the tile and in the health line, and recovery. keywords.selftest.js pins the matching rule (8 cases). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>The "TAP IS ON · since …" line (and its SWITCHING / NOT SWITCHED forms, and the red error line under the destination) are gone from the tile. A tile shows the keyword Tap is on, its destination, the groups moved, the slide chip and the trail; status is the corner dot's job, as everywhere else in ProdDash. The dot now follows the retries honestly: a switch Overflow refuses is retried three times, ten seconds apart, with the dot amber ("retrying (n of 4 attempts failed)") while the module is still fixing it itself; only when the attempts are spent does the dot go red with the reason on hover, and the alert fire if it is switched on. The budget counts refusals in a row across keywords, not per keyword — a service keeps bringing new keywords, and each one restarting a fresh budget meant a dead Overflow never turned the dot red. The alert is raised once per outage, and a success clears the streak. Verified against the mock with --fail-switch: amber through three refusals, red on the fourth with one alert, later refusals stay red without re-alerting, and green again on the first success once the mock is back. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>