Remlo has three distinct messaging surfaces. Don’t confuse them — each has a different cadence, audience, and tooling:Documentation Index
Fetch the complete documentation index at: https://docs.remlo.xyz/llms.txt
Use this file to discover all available pages before exploring further.
| Channel | Audience | Trigger | Lives in |
|---|---|---|---|
| Operational notifications | One employer | Server event (payroll, escrow, KYC) | Dashboard bell |
| Per-employee email receipts | Individual employee | Payroll run settles | Employee inbox |
| System announcements | Operator-chosen audience | Operator publishes manually | Top-of-page banner |
What an announcement is
A row insystem_announcements with:
title(1–120 chars)body(1–600 chars)severity—info/success/warning/error. Drives the banner color.audience—all/employers/employees/admins. Server-side scoped on every read.published_at— when it goes live.null= draft, not visible.expires_at— when it auto-clears.null= no auto-expiry.link_url+link_label— optional CTA in the banner.created_by— the platform admin who authored it. Set automatically.
Authoring
- Sign in as a platform admin (your Privy
submust be in theADMIN_USER_IDSenv var, comma-separated). - Navigate to
/admin/announcements. - Click “New announcement”.
- Fill in:
- Title — short, scannable.
- Body — one or two sentences.
- Severity — match the visual urgency to the actual urgency. Reserve
errorfor active outages. - Audience — pick the smallest meaningful slice.
employersis the default for product updates;allcasts wider but increases ambient banner-fatigue. - Link URL + label — optional. Use a
/-rooted path for in-app destinations, full URL for external. - Expires at — optional. Use this for any time-bound message (maintenance windows, incidents). For evergreen announcements like “Mainnet is live”, leave blank.
- Click Publish. The banner appears immediately on every matching session that fetches
/api/announcements/active(next page load, or up to 60s on a static tab).
Audience semantics
The audience filter is enforced server-side using the user’s role as Remlo sees it — not what the client claims:audience | Visible to |
|---|---|
all | Anyone with a logged-in session (employers, employees, admins). |
employers | Users with an active employers row. |
employees | Users with an active employees row. |
admins | Users in ADMIN_USER_IDS. |
Severity ordering and stacking
If multiple announcements are simultaneously active for a user, the banner shows up to 5, ordered:- By severity:
errorfirst, thenwarning, thensuccess, theninfo. - Within a severity, by recency (newest first).
expires_at set.
Lifecycle
- Edit an existing announcement via
PATCH /api/admin/announcements/{id}(admin UI button TBD — for now the field-level edit is API-only). Editing does NOT re-deliver to users who already dismissed; treat dismissals as permanent. To re-deliver an updated message, publish a new announcement. - Expire by setting
expires_atin the past, or by deleting the row entirely. - Delete via
DELETE /api/admin/announcements/{id}. The dismissals row cascades.
When NOT to use this channel
System announcements are not for:- Operational events (payroll completed, KYC approved, escrow settled). Those go through
notificationsand the dashboard bell, fired by deterministic server events. They’re per-employer; this channel is platform-wide. - Marketing email (launch announcements, product updates to the waitlist). Those go through Resend Audiences + Broadcasts, where the audience is the confirmed waitlist signups, not in-app users.
- Per-employee comms (your KYC needs renewing, your payslip is ready). Those go through transactional email templates (emails/).
API surface
For automation or scripting, the same flows are reachable directly:POST /api/admin/announcements— create. Admin-only.PATCH /api/admin/announcements/{id}— edit. Admin-only.DELETE /api/admin/announcements/{id}— remove. Admin-only.GET /api/admin/announcements— list everything (drafts, expired, active). Admin-only.GET /api/announcements/active— list visible-to-this-user banners. Authenticated.POST /api/announcements/{id}/dismiss— per-user dismissal. Authenticated, idempotent.
sub is in ADMIN_USER_IDS. The user endpoints require any authenticated session and resolve the audience server-side.