Alerts
Posts new job listings and upcoming hackathons to Discord. An org adds feeds. Each feed reads one source on a schedule and posts the new items to one Discord webhook.
Feeds
| Kind | Source | Item key |
|---|---|---|
github_jobs | The job table in a GitHub repo's README (Company, Role, Location, Application/Link, Date Posted), as in the internship and new grad lists | A hash of company, role, first location, and link with no tracking parameters |
hackathons | Hack Club, Euro-Hackathons and Hackalist | The event name, in lowercase |
The first run of a feed records all current items and posts none. Thus a new feed does not post hundreds of old listings. Later runs post up to 25 new items, with half a second between posts. If a post fails, the item stays new and goes out on the next run.
Routes
All routes are under /api/alerts/<org>, for officers of the org. The alerts switch turns them off.
| Route | Does |
|---|---|
GET /feeds | Each feed with its schedule, last run, last error and number of posts |
GET /feeds/<key> | One feed |
PUT /feeds/<key> | Creates (201) or updates (200) a feed. Body below |
DELETE /feeds/<key> | Deletes the feed, its posted items and its webhook secret |
GET /feeds/<key>/history | The last 50 runs (time, duration, items found, new and posted, error) and the last 50 items the feed posted or recorded |
POST /feeds/<key>/run | Starts a run now and returns 202. With {"post_existing": true}, a first run posts the current items |
GET /presets | The feeds that packs offer, with the kind and config to send to PUT /feeds/<key>, and added when the org has a feed with that key |
{
"kind": "github_jobs",
"webhook_url": "https://discord.com/api/webhooks/<id>/<token>",
"every_hours": 3,
"enabled": true,
"config": {"repo": "vanshb03/Summer2026-Internships", "label": "Internship"}
}- The key has lowercase letters, digits and dashes, 40 characters or fewer.
- A new feed needs
webhook_url. Platform keeps it as the org secretalert_webhook_<key>and never returns it. A route shows only if it is set. - You cannot change
kindafter you create the feed. every_hoursis 1 to 168, default 3.- On the Alerts page of the dashboard, New feed > Start from fills the form from a pack feed, such as the internship and hackathon feeds of the
careerspack.
github_jobs config:
| Field | Default | Does |
|---|---|---|
repo | required | owner/name on GitHub |
branch | main | The branch to read |
path | README.md | The Markdown file with the table |
label | Job | The word in the post title, such as Internship |
skip_closed | true | Leaves out roles marked closed |
max_age_days | 2 | Leaves out rows with an older Date Posted. 0 turns off this check |
hackathons config:
| Field | Default | Does |
|---|---|---|
sources | all three | One or more of hackclub, euro_hackathons, hackalist |
min_days_ahead | 0 | Leaves out events that start sooner |
max_days_ahead | 90 | Leaves out events that start later |
A hackathon feed fails only if all its sources fail. MLH and Devpost refuse automated requests, so they are not sources.
Schedule
The alerts.run_due job runs every 15 minutes. It runs each enabled feed whose every_hours has passed since its last run, in orgs that have the module on. A failed run keeps its reason in last_error. Each run, scheduled or started with Run now, adds a row to alert_runs; Platform keeps the last 50 for each feed. The next run occurs at the next due time.
Move from a webhook script
- If the module is off for the org, turn it on:
flask --app main org modules <prefix> --on alerts. - Make a Discord webhook for each channel, or use the webhooks you have.
PUTone feed for each source. The first run records the current listings, so nothing is posted two times.- Stop the schedule of the old script.