# V1 API compatibility
The service exposes the legacy /api/Notification/* surface in full, alongside the V2 /v1/* API, so existing V1 clients keep working with no code changes — only their base URL moves to this service. V1 endpoints are marked deprecated in the OpenAPI docs but there is no removal date until every caller has migrated.
Both APIs read and write the same notifications: a notification created through V1 appears in V2 lists and vice versa. You can migrate consumers and producers independently.
# Endpoint mapping
| V1 endpoint | Behaviour | V2 equivalent |
|---|---|---|
POST /api/Notification/Create/{user} |
Create for {user} from the V1 body |
POST /v1/notifications |
POST /api/Notification/CreatePayload/{user}?payload-type=X&entity=Y |
Create from a configured template | POST /v1/notifications |
PUT /api/Notification/SetViewed/{notification} |
Mark read | POST /v1/notifications/{id}/read |
PUT /api/Notification/SetUnread/{notification} |
Mark unread | POST /v1/notifications/{id}/unread |
PUT /api/Notification/Remove/{notification} |
Delete (see semantics below) | DELETE /v1/notifications/{id} |
DELETE /api/Notification/ForceRemove/{notification} |
Delete | DELETE /v1/notifications/{id} |
# Response envelope
V1 endpoints always answer with the classic envelope — never problem+json:
// success
{ "value": { "id": "3f9e4c1a-…" }, "errors": [], "warnings": [] }
// mutation success
{ "value": true, "errors": [], "warnings": [] }
// failure (HTTP status is also set: 400/401/403/404)
{ "value": null, "errors": ["Forbidden: publishing requires a publisher group or scope"], "warnings": [] }
CreatePayload responds { "errors": [], "warnings": [] } with no value, matching V1.
# Auth
Same OIDC bearer tokens as V2 (see cognito-setup.md):
Create/CreatePayloadrequire a publisher token (PUBLISHER_GROUPS/PUBLISHER_SCOPES) — the V1 service’s implicit trust is replaced by real authorization.SetViewed/SetUnread/Remove/ForceRemoveact on the caller’s own notifications (tokensub); anyone else’s return404withvalue: false.
# Field mapping for Create/{user}
| V1 field | Where it goes in V2 |
|---|---|
{user} path parameter |
userGuid (the recipient — this wins over payload.userId; a mismatch adds a warning to warnings) |
payload.title |
title (required) |
payload.description |
body (falls back to the title when absent) |
originatorId, recipientId, status, payload.category, payload.icon, payload.userId, payload.entityId + any extra fields |
preserved verbatim under metadata.v1 |
| — | severity is info; source is derived from the caller’s token |
Unknown extra fields are accepted (V1 clients were never strict) and preserved in metadata.v1.
# CreatePayload templates
V1 rendered notifications server-side from a payload type. V2 keeps that working via the
V1_PAYLOAD_TEMPLATES env var — a JSON map of payload-type → template, with {entity}
substituted from the entity query parameter:
// wrangler.toml var or .dev.vars — one line of JSON
V1_PAYLOAD_TEMPLATES = '{
"assessment_assigned": {
"title": "New Assessment Available",
"body": "You have been assigned assessment {entity}",
"severity": "info", // optional, default "info"
"actionUrl": "/assessment/{entity}" // optional
}
}'
- Payload types without a template still work: the notification gets a humanized title (
assessment_assigned→ “Assessment assigned”). - Calls are idempotent per (user, payload-type, entity) — a repeated call creates no second notification (this dedupe is an upgrade over V1; V1 duplicated on retry).
payloadTypeandentityIdare stored inmetadata.v1.
# Intentional semantic differences
Small, documented deltas — none observable through the V1 API surface above:
Removeis a real delete. V1 called it a “soft delete”, but exposed no API to list or restore soft-deleted rows, so hard delete is observably identical. If restore functionality existed in your V1 deployment, raise it before migrating.SetViewedafterRemovereturns 404 (row is gone) rather than whatever V1 did with a soft-deleted row.- Notification ids are UUIDs issued by this service. Clients that treated ids as opaque strings (all known ones) are unaffected.
CreatePayloadretries don’t duplicate (see above).- HTTP status codes accompany the envelope on failures (400/401/403/404). V1 clients that only read the envelope see identical bodies; clients that check status codes get more accurate ones.
# Migration path off V1
- Point V1 clients at this service (base URL change only) — verify with the envelope responses above.
- Move producers to
POST /v1/notificationsto gaindedupeKey,severity,actionUrl, andmetadatacontrol — or better, adopt@inovus-medical/notifications. - Move consumers to the npm client (
list/subscribe/markRead…). - When the access logs show no more
/api/Notification/*traffic, deletesrc/v1compat.ts, its tests, and this file.