Nobody has started this yet — be first.
Business impact
POST /notifications publishes to exactly one user_id per call. There is no endpoint for notifying a list of users or everyone at once -- anything that wants to announce something to more than one user, such as a maintenance-window notice or a shared-document update, has to loop over POST /notifications itself, once per recipient, from the caller's side. That's N round trips for something that's conceptually one event, and it pushes retry and partial-failure handling onto every caller instead of handling it once, here.
Problem
There is currently no route for publishing the same event to more than one user_id in a single call; POST /notifications only accepts a single user_id.
Current behavior
POST /notifications/broadcast with user_ids=['alice', 'bob'] returns 404 -- the route doesn't exist yet.
Expected behavior
POST /notifications/broadcast accepts {"user_ids": [...], "event": ..., "data": ...} and publishes the same event/data to every listed user_id -- one call, one event, fanned out server-side to each recipient's own stream and backlog exactly as if publish_event() had been called once per user_id. The response reports the Redis stream entry id assigned for each recipient.
Steps to reproduce
pytest practice_tickets/tests/test_ticket_09_broadcast_endpoint.py -vWhy this matters
This is mostly a matter of looping publish_event() over user_ids and shaping the request/response schema -- the interesting design question is what the response should look like when there's more than one recipient (a dict keyed by user_id, since each publish gets its own independent stream entry id) and what a caller should be able to assume about partial failure.
Suggested approach
Look at how app/routers/notifications.py::publish_notification calls publish_event() for a single user_id, and how PublishRequest / PublishResponse are shaped in app/schemas.py. A broadcast is the same operation repeated across a list -- the new request model needs user_ids: list[str] instead of user_id: str, and the response needs to carry one id per recipient instead of one id total.
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket_09_broadcast_endpoint.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b feat/broadcast-endpointFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/broadcast-endpointSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inhttp://localhost:8000http://localhost:8001Authorization: Bearer <user_id>X-User-Id: <user_id>curl localhost:8000/healthz
curl -N -H "Authorization: Bearer alice" localhost:8000/notifications/stream
# watch `: heartbeat` lines arrive roughly every 15s with no other traffic
Work the tickets in practice_tickets/tickets/ (ordered easy → hard); each names one dedicated pytest test in practice_tickets/tests/:
pytest practice_tickets/tests/test_ticket_01_heartbeat_default_margin.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 15, clean pass/fail summary
Tickets 14 and 15 need genuinely concurrent, long-lived streams, which the ASGI test transport used everywhere else in this project can't represent -- their tests spin up a real (short-lived) uvicorn server on a loopback socket instead (LiveServer in practice_tickets/tests/conftest.py).
Level 2
Implement a feature
Extend the system within its own patterns.