0% completed
Notification System: API Design
On This Page
Step 4: API Specifications
Step 4: API Specifications
1. Send Notification
POST /v1/notifications- Headers:
Authorization: Bearer <tenant-key> - Request Body:
{ "user_id": "u_12345", "template_id": "order_shipped_v1", "channels": ["push", "email"], "priority": "transactional", "idempotency_key": "uuid-gen-by-client", "data": { "order_id": "999", "name": "Alice" } }
- Response:
202 Accepted
{ "notification_id": "notif_abc123", "channels": ["push", "email"] }
- Error:
429 Too Many Requests(quota exceeded).
Four fields decide how the rest of the design behaves.
channels is a list, and it is optional. Step 2 asks for delivery on one or several channels, so a single channel field could not express the requirement. Sending the list explicitly is the common case. Leaving it out means "use whatever this user has enabled", which is what a caller wants when it does not know or care how the person prefers to be reached. Either way the Preference Service in Step 5 makes the final decision: a channel the user has switched off is dropped from the list before anything is queued.
priority names urgency, not a channel. Step 2 says channels are never ranked against each other, and that still holds: email is not served ahead of SMS. Urgency is a separate question, and it is one the system has to answer. The accepted values are transactional, default and bulk. Step 7 gives each of them its own queue per channel, so a one-time passcode is never stuck behind a ten million message campaign.
idempotency_key is generated by the caller, one value per real-world event, and reused on every retry of that event. Delivery here is at least once, so a caller that retries after a timeout would otherwise send the same message twice. The service stores the key with its result for 24 hours and returns the original notification_id for a repeat.
202 Accepted, not 200 OK. The response means the request is queued, not that anybody has been notified. Delivery happens later and can still fail. Returning 200 would imply a promise this endpoint cannot make.
2. Get Status
GET /v1/notifications/{notification_id}- Response:
{ "status": "partial", "channels": { "push": { "status": "delivered", "updated_at": "..." }, "email": { "status": "failed", "reason": "hard_bounce" } } }
Status is reported per channel, because one notification can succeed on push and fail on email. A single top-level status would have to pick one of them and hide the other, and the failed one is usually the one the caller needs.
3. Update Preferences
PUT /v1/users/{user_id}/preferences- Body:
{ "categories": { "marketing": false, "shipping": true } }
Opting out is a PUT on the user's own preferences rather than a flag on a notification, so it applies to every future send from every caller. A tenant cannot override it by setting a field on a request.
Next: Step 5, where the services behind that API are laid out.
On This Page
Step 4: API Specifications