PrimeBrickPrimeBrick
  • Docs
  • Contact
  • MIT License
  • Documentation
  • MCP Server
  • API Catalog
  • Services
  • Libraries
PrimeBrickPrimeBrick

© 2026 PrimeBrick. MIT License. v3.11.2

github

Webhook Ingress

The webhook microservice exposes a provider-agnostic HTTP ingress. It verifies an API key, confirms that the target service is enabled and online, then publishes the JSON payload to NATS JetStream. The target microservice receives the event and remains responsible for validating its business meaning, authenticating the forwarded credential, and enforcing its own permissions.

  • Package: primebrick-webhook
  • Service code: webhook
  • Runtime: Bun (bun --hot src/index.ts in development; bun dist/index.js in production)
  • Database: webhook schema — owns only its config_entries table; API-key and service-registry lookups are delegated to the Backend over NATS

HTTP route

The custom route is:

Code
POST /webhook/{service_code}/{intent...}

service_code identifies the registered target microservice. The optional intent is the remaining path, forwarded as a string; the webhook service does not interpret it. The request body must be valid JSON. An empty body is forwarded as null.

For example, a request to /webhook/emailsender/delivery/status with a JSON body is queued for service emailsender with intent delivery/status.

The JetStream message body has this shape:

Code
{ "service_code": "emailsender", "intent": "delivery/status", "received_at": "2026-01-01T12:00:00.000Z", "body": { "event": "delivered" } }

The original Authorization header is forwarded as a NATS header so the target service can authenticate the caller again. Other HTTP headers are not forwarded.

The ingress returns HTTP 200 after JetStream acknowledges the publish:

Code
{ "received": true, "seq": "42" }

The seq value is the JetStream publish sequence serialized as a string. A publish failure returns HTTP 502 so the external provider can retry.

Authentication and authorization

Every webhook request must include an API key in the Authorization header, using either ApiKey <key> or Bearer <key> when the key starts with pbk_. The Backend verifies the key hash and returns its active state, expiry, and permission metadata over NATS request/reply. The webhook service caches positive results for 60 seconds and negative results for 10 seconds (up to 1,000 entries).

The ingress precheck is not the target service's final authorization decision. The target receives the original Authorization value and must re-verify it and apply its own RBAC rules for the requested operation.

Target routing

The service keeps an in-memory cache from the Backend's lifecycle subjects:

  • service.register and service.heartbeat update the service's online state.
  • service.unregister removes the cached service.
  • service.stale marks it stale.

A positive online cache entry is routable. Other states are checked against the Backend with the service.registry.get request/reply subject using { "code": "<service_code>" }. The registry row must be enabled and online. Unknown service codes or lookups that cannot resolve a registry row return HTTP 404; known but disabled or unavailable services return HTTP 502.

NATS delivery

At startup, the service ensures a JetStream stream named WEBHOOK with the subject filter webhook.>. For each accepted request, it publishes to:

Code
webhook.<service_code>.received

The stream's delivery consumers belong to the target microservices. This ingress does not call a target service's business endpoint directly and does not provide provider-specific signature validation.

HTTP errors

Errors use the RFC 7807-style JSON shape with type, title, status, detail, internal_code, and severity fields.

StatusCondition
400Request body is not valid JSON
401Authorization header is missing, invalid, inactive, or expired
404Target service code is unknown, or the registry lookup could not resolve a service row
500API-key dependencies were not initialized
502Target service is known but disabled/unavailable, the Backend lookup fails, or JetStream publish fails

Configuration and deployment

The service uses createMicroservice. Following the env policy, only the database bootstrap comes from env:

VariableRequiredDefaultPurpose
DATABASE_URLyes—PostgreSQL connection string (owns the webhook.config_entries table)
DATABASE_SCHEMAyes—Declared schema domain (webhook; CSV/JSON array for multi-schema — [0] is owned)

Everything else lives in webhook.config_entries and is loaded by ConfigLoader at startup: nats_url, service_code, http_port, service_base_url (public URL advertised in the registry), and service_client_shield_key.

For local development and build commands:

TerminalCode
cd webhook pnpm run dev pnpm run build pnpm run start

The SDK exposes the public /health route and manages service registration, periodic heartbeats, NATS connection lifecycle, and graceful shutdown. The service owns the webhook PG schema (migrations under db-meta/patches/), so the health adapter checks the database via the DAL; NATS connectivity is monitored by the SDK.

The service's HTTP surface is declared once in src/controllers/http/router.ts via makeServiceRoutes — the same webhookRoutes table drives both the ingress router and the OpenAPI spec served at GET /api/v1/openapi.json (client-shielded; the BE fetches it during service discovery).

Next steps

  • Microservice architecture
  • API path conventions
  • EmailSender microservice
  • AI microservice
Last modified on October 28, 2026
On this page
  • HTTP route
  • Authentication and authorization
  • Target routing
  • NATS delivery
  • HTTP errors
  • Configuration and deployment
  • Next steps
JSON
JSON