WebhookGatewayService¶
Target
This page describes intended behaviour or structure. No implementation is claimed unless a section is explicitly marked Current implementation or Evidence.
| Bounded context | BC-17 Connectors & Webhooks |
| Platform / plane | Integration Platform / Capability & Tools |
| Deployment granularity | standalone host |
| Status | target · ConnectSoft.Factory.Integration.Webhooks |
| Built from | connectsoft-apigateway · libraries: connectsoft-extensions-rate-limiting, connectsoft-extensions-saas-abstractions, connectsoft-extensions-messaging-model, connectsoft-extensions-persistence-model-nhibernate, connectsoft-extensions-observability |
| Decisions | ADR-0012, ADR-0017 |
| Traceability | FR-05 FUX-02 NFR-02 NFR-09 NFR-18 XL-16 |
Purpose and responsibilities¶
Inbound webhooks are the Factory's only unauthenticated-by-token public surface, so they get the smallest possible host: WebhookGatewayService is built from the API gateway template, runs as a separate conditional deployment (only when a tenant subscribes to vendor events), and does three things per delivery: identify the ingress and its connector scheme, verify the signature and the replay window, deduplicate and capture the payload by hash. It then emits a verified delivery for ConnectorRegistryService and answers the vendor; it never dispatches to a workflow or a target and holds no credential beyond the signing secrets brokered per connector.
Context and boundaries¶
flowchart LR
S["WebhookGatewayService"]
ext_generated_products["ext-generated-products"] -->|sync-http| S
S -->|event| ConnectorRegistryService["ConnectorRegistryService"]
S -->|grpc| ArtifactService["ArtifactService"]
S -->|grpc| SecretBrokerService["SecretBrokerService"]
S -. "InboundDeliveryVerified" .-> E_InboundDeliveryVerified(("event"))
S -. "InboundDeliveryRejected" .-> E_InboundDeliveryRejected(("event"))
Domain model¶
InboundDelivery — One inbound HTTP delivery at a subscription's ingress identity — headers hash, signature check, timestamp and replay window, deduplication key, payload capture and the verification outcome. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
inboundId |
guid |
yes | |
tenantId |
string |
yes | |
ingressRef |
string |
yes | |
subscriptionId |
guid |
no | |
connectorId |
string |
no | |
externalId |
string |
no | |
dedupKey |
string |
no | |
signatureValid |
bool |
yes | |
outcome |
enum:VerificationOutcome |
yes | |
payloadRef |
string |
no | |
payloadHash |
hash |
no | |
receivedAt |
datetime |
yes |
Invariants:
- A delivery is forwarded only when the ingress is known, the signature verifies under the connector's scheme and the timestamp is inside the replay window; everything else is rejected with its outcome and no payload is dispatched.
- Payloads are captured by hash to the artifact store and never logged.
- The gateway is stateless beyond the deduplication cache; the connector module holds the delivery lifecycle.
Events: InboundDeliveryVerified, InboundDeliveryRejected · Tables: InboundDeliveries · Repository: InboundDeliveryRepository
erDiagram
InboundDelivery {
guid inboundId PK
string tenantId
string ingressRef
guid subscriptionId
string connectorId
string externalId
string dedupKey
bool signatureValid
enum_VerificationOutcome outcome
string payloadRef
hash payloadHash
datetime receivedAt
}
Entities and value objects¶
None modelled
No entities or value objects are modelled for this item yet.
Data model and storage¶
| Store | Role | Engine / profile | Tables |
|---|---|---|---|
| Integration store | primary | azure-sql / nhibernate-sqlserver | InboundDeliveries |
| Artifact content store | blob | blob / azure-blob | — |
| Cache and coordination | cache | redis / azure-cache | — |
| Table | Owner | Columns | Indexes |
|---|---|---|---|
dbo.InboundDeliveries |
InboundDelivery | 12 | 1 |
Full DDL: appendix.
APIs¶
| Method | Path | Operation | Scope | Side effects | Idempotency | Gateway | Summary |
|---|---|---|---|---|---|---|---|
POST |
/webhooks/{ingressRef} |
ReceiveWebhook | factory.webhooks.receive |
shared-write | key-header | GW-06 | Vendor webhook endpoint per subscription ingress identity; verifies and captures, returns 202 or the rejection status. |
GET |
/webhooks/health |
WebhookHealth | factory.health.read |
read | natural | GW-06 | Liveness and readiness of the ingress. |
None modelled
No gRPC methods are modelled for this item yet.
Messages¶
| Event | Topic | Scope / v | Aggregate | Consumers | Summary |
|---|---|---|---|---|---|
| InboundDeliveryVerified | connectors.ingress.v1.inbound-delivery-verified |
integration / 1 | InboundDelivery | ConnectorRegistryService, DashboardService | A delivery passed signature and replay checks and its payload is captured. |
| InboundDeliveryRejected | connectors.ingress.v1.inbound-delivery-rejected |
integration / 1 | InboundDelivery | AuditService, DashboardService | A delivery was rejected with its outcome. |
InboundDeliveryVerified payload:
| Field | Type | Required | Description |
|---|---|---|---|
inboundId |
guid |
yes | |
tenantId |
string |
yes | |
subscriptionId |
guid |
yes | |
connectorId |
string |
yes | |
externalId |
string |
yes | |
dedupKey |
string |
yes | |
payloadRef |
string |
yes | |
payloadHash |
hash |
yes |
InboundDeliveryRejected payload:
| Field | Type | Required | Description |
|---|---|---|---|
inboundId |
guid |
yes | |
tenantId |
string |
yes | |
ingressRef |
string |
yes | |
outcome |
enum:VerificationOutcome |
yes |
| Command | Handled by | Result events | Summary |
|---|---|---|---|
ReceiveWebhook |
InboundDelivery | InboundDeliveryVerified, InboundDeliveryRejected |
| Event | Producer | Handler | Purpose |
|---|---|---|---|
| ConnectorSubscriptionChanged | ConnectorRegistryService | RefreshIngressTable |
Know which ingress identities are active and which scheme verifies them. |
Workers and jobs¶
None modelled
No workers are modelled for this item yet.
Actors and grains¶
None modelled
No grains are modelled for this item yet.
Sagas and long-running flows¶
None modelled
No sagas are modelled for this item yet.
Options and configuration¶
| Section | Key | Type | Default | Reloadable | Secret | Summary |
|---|---|---|---|---|---|---|
Integration:Webhooks |
MaxPayloadKb |
int | 1024 |
yes | no | Payload size limit. |
Integration:Webhooks |
ReplayWindowMinutes |
int | 5 |
yes | no | Timestamp tolerance for signed deliveries. |
Integration:Webhooks |
RatePerMinutePerIngress |
int | 600 |
yes | no | Rate limit per ingress identity. |
Metrics and observability¶
| Metric | Instrument | Unit | Dimensions | Summary |
|---|---|---|---|---|
connectsoft.factory.integration.webhooks_received |
counter | {delivery} | connector, outcome | Inbound deliveries by outcome. |
connectsoft.factory.integration.webhook_latency |
histogram | s | connector | Verification latency. |
Security¶
| Authentication | none |
| Scopes | factory.webhooks.receive (Vendor-signed deliveries (signature is the authentication).); factory.health.read (Health probes.) |
| Roles | Observer |
| Tenant isolation | row-filter |
| Data classification | confidential |
| Max side-effect class | shared-write |
| Secrets | webhook-signing-secrets (key-vault, rotate 90 d) |
The signature is the authentication: an unknown ingress, a bad signature, a timestamp outside the replay window, an oversized payload or a rate-limited sender is rejected with its outcome and no payload reaches any target. Payloads are captured to the artifact store by hash and never logged.
Dependencies¶
Outbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| ConnectorRegistryService | event | Verified deliveries to record and dispatch. | queue | yes |
| ArtifactService | grpc | Payload capture by hash. | fail-fast | yes |
| SecretBrokerService | grpc | Signing secrets per connector scheme. | fail-fast | yes |
Inbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| ext-generated-products | sync-http | Vendor and product webhooks. | — | yes |
Failure and recovery¶
| Scenario | Detection | Recovery | Data impact | RTO (min) |
|---|---|---|---|---|
| Signing secret rotated at the vendor | BadSignature rate rises for one connector | rotation task on the connection; deliveries rejected until rotated, vendor retries | delayed deliveries | 60 |
| Ingress flood | RateLimited outcomes | per-ingress rate limits; vendor backoff; no payload loss for accepted deliveries | none | 0 |
NFR targets¶
| Target | Class value (pilot-interactive) | Override |
|---|---|---|
| latency_p95_ms | 2000 | — |
| availability_pct | 99.5 | — |
| throughput_rps | 50 | — |
| rpo_minutes | 15 | — |
| rto_minutes | 240 | — |
| cost_class | low | — |
Deployment¶
| Granularity | standalone host |
| Topologies | saas-default, hybrid |
| Runtime | container-apps; replicas 1–8 |
| Ports | https 7950 |
Deployed on Azure Container Apps in the saas-default and hybrid profiles only; an on-prem installation uses polling subscriptions instead of inbound webhooks unless the customer exposes an ingress.
Current implementation¶
ConnectSoft.ApiGatewayTemplate exists; no webhook ingress, scheme verification or capture exists.
Later capabilities¶
Vendor-specific replay negotiation; signed acknowledgements; ingress per region.
Decisions and traceability¶
| Family | Ids |
|---|---|
| FR | FR-05 |
| FUX | FUX-02 |
| NFR | NFR-02, NFR-09, NFR-18 |
| XL | XL-16 |