Skip to main content

Notification Service — API Contracts

Status: populated Owner: Platform Engineering Last updated: 2026-04-18

This service has no public REST API and is not routed via Kong. All endpoints are cluster-internal only, accessible by admin-dashboard and health probes.

1. Internal Endpoints (no Kong route)

All on port 3030. Reachable only within the Kubernetes cluster.

Health & Metrics

MethodPathPurpose
GET/health/liveLiveness probe
GET/health/readyReadiness (PG + NATS + SendGrid reachable)
GET/metricsPrometheus metrics

Notification Log (admin-dashboard)

GET /internal/notifications

Query notification log. Params: accountId, channel, status, from, to, cursor, limit (max 100).

Response 200:

{
"data": [
{
"notificationId": "uuid",
"channel": "EMAIL",
"category": "BILLING",
"status": "SENT",
"sourceEventType": "billing.invoice.generated.v1",
"recipientAddress": "***@example.com",
"sentAt": "2026-04-01T00:12:00Z",
"attemptCount": 1
}
],
"nextCursor": "opaque"
}

Notification Preferences (admin-dashboard)

GET /internal/preferences?accountId={uuid}

Returns all preferences for an account.

PUT /internal/preferences

Set or update a preference.

{
"accountId": "uuid",
"category": "OPERATOR_ALERT",
"channel": "SMS",
"optedOut": true
}

Response 200: updated preference.

Template Management (admin-dashboard)

GET /internal/templates

Params: type, channel, isActive.

GET /internal/templates/{id}

Full template with bodyHtml, bodyText, variablesSchema.

POST /internal/templates

{
"type": "INVOICE_GENERATED",
"channel": "EMAIL",
"subject": "Your invoice for {{periodStart}} is ready",
"bodyHtml": "<mjml>...</mjml>",
"bodyText": "Your invoice {{invoiceId}} for period {{periodStart}} - {{periodEnd}} is ready.",
"variablesSchema": {
"type": "object",
"required": ["invoiceId", "periodStart", "periodEnd", "subtotalAmount", "currency", "downloadUrl"],
"properties": {
"invoiceId": { "type": "string" },
"periodStart": { "type": "string" },
"periodEnd": { "type": "string" },
"subtotalAmount": { "type": "string" },
"currency": { "type": "string" },
"downloadUrl": { "type": "string", "format": "uri" }
}
}
}

Response 201.

PATCH /internal/templates/{id}

Updates template; increments version; previous version readable via audit log.

POST /internal/templates/{id}/preview

Body: { "variables": { ... } }. Returns rendered HTML for email preview in admin-dashboard. Response 200: { "html": "...", "text": "..." }.

2. Error Response Shape

{
"type": "https://errors.ghasi.io/notif/TEMPLATE_NOT_FOUND",
"title": "No active template found",
"status": 404,
"code": "TEMPLATE_NOT_FOUND",
"detail": "No active notification_templates row for type=INVOICE_GENERATED, channel=EMAIL",
"requestId": "req_01H..."
}

3. Caller Authentication (Internal)

Internal endpoints are protected by a shared X-Internal-Token header (Vault-injected into admin-dashboard). Not exposed via Kong; network policy restricts access to cluster-internal pods only.