Skip to main content

Notification API Tenant Context

Summary​

Tenant identity on the native surface is a caller-supplied string parameter, never a validated claim. It is applied by some endpoints and ignored by others. There is no global query filter and no ambient tenant accessor.

Audience​

Security reviewers, engineers, architects, QA and support.

How tenant enters a request​

There are exactly three verified paths.

  1. Query parameter — the inbox endpoint and the template list accept an optional tenantId query value.
  2. Request body field — template create and both test hooks carry a tenant identifier in the JSON body.
  3. Message metadata — for event-driven processing, tenant arrives on the message header rather than through HTTP.

No path validates the supplied value against an authenticated principal, a token claim, a registered tenant list or a request header. Any caller may name any tenant.

Per-endpoint tenant behavior​

EndpointTenant behavior
GET /notifications/myApplied — matched directly against the recipient row's tenant
POST /notifications/{id}/readNot applied — lookup by identifier alone
GET /notification-templatesApplied only when supplied and non-blank; otherwise unfiltered across tenants
POST /notification-templatesApplied — a blank value is stored as null, creating a global template
PUT /notification-templates/{id}Not applied — lookup by identifier alone
POST /notification-templates/{id}/testInherited from the template row
GET /notifications/{id}Not applied
GET /notifications/{id}/attemptsNot applied
POST /notifications/{id}/retryNot applied
GET /notifications/failedNot applied — spans all tenants
GET /notifications/processed/{eventId}Not applied
GET /notifications/auditNot applied — spans all tenants
GET /notifications/statsNot applied — platform-wide counts
POST /notifications/test/dispatchCarried onto the envelope
POST /notifications/test/publishCarried into message metadata
GET / and GET /healthNot applicable

Six of the fifteen resource operations carry tenant in some form — applied, inherited or forwarded onto the event. The remaining nine apply no tenant predicate at all. The two operational endpoints are not applicable.

Null tenant semantics​

A null tenant is meaningful rather than absent. On the template model it denotes a global default template available to every tenant, and the resolution order prefers a tenant-specific row over the global one. On the recipient model, a request that omits tenantId matches recipient rows whose tenant is null, so omitting the parameter does not broaden the search — it narrows it to the global partition.

Callers that omit the parameter expecting "all tenants" will instead receive only null-tenant rows.

No global filter​

The persistence context declares no global query filter for tenant. Scoping is applied by each query individually, which is why the behavior varies by endpoint. Adding an endpoint without a tenant predicate produces a cross-tenant read silently, with no compile-time or runtime signal.

Compatibility surface​

The monolith derives tenant implicitly. The caller is resolved from the email claim to a user record, and notification rows are matched on that user, so cross-tenant reads are prevented as a side effect of caller scoping rather than by an explicit tenant predicate. Rows additionally carry a nullable client tenant identifier which is projected into the response.

Classification​

Partial.

Requires confirmation​

Whether a validated tenant claim or header is planned for the native surface, and whether the unscoped administration endpoints are intended to remain cross-tenant, require confirmation.

See Also​

Keywords​

  • Notification API
  • Tenant Context
  • Draft API documentation

Source References​

  • microservices/src/notification-service/Api/NotificationEndpoints.cs
  • microservices/src/notification-service/Infrastructure/NotificationDbContext.cs
  • microservices/src/notification-service/Domain/NotificationEntities.cs
  • Controllers/NotificationsController.cs

Revision Information​

  • Status: Draft
  • Last reviewed: 2026-07-21
  • Review cycle: Quarterly