# 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 / CreatePayload require a publisher token (PUBLISHER_GROUPS / PUBLISHER_SCOPES) — the V1 service’s implicit trust is replaced by real authorization.
  • SetViewed / SetUnread / Remove / ForceRemove act on the caller’s own notifications (token sub); anyone else’s return 404 with value: 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).
  • payloadType and entityId are stored in metadata.v1.

# Intentional semantic differences

Small, documented deltas — none observable through the V1 API surface above:

  1. Remove is 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.
  2. SetViewed after Remove returns 404 (row is gone) rather than whatever V1 did with a soft-deleted row.
  3. Notification ids are UUIDs issued by this service. Clients that treated ids as opaque strings (all known ones) are unaffected.
  4. CreatePayload retries don’t duplicate (see above).
  5. 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

  1. Point V1 clients at this service (base URL change only) — verify with the envelope responses above.
  2. Move producers to POST /v1/notifications to gain dedupeKey, severity, actionUrl, and metadata control — or better, adopt @inovus-medical/notifications.
  3. Move consumers to the npm client (list/subscribe/markRead…).
  4. When the access logs show no more /api/Notification/* traffic, delete src/v1compat.ts, its tests, and this file.