An e-commerce access review becomes hard to sign when fraud, fulfillment, finance, and customer messaging all hold the marketplace webhook credential. TL;DR: register one endpoint, verify deliveries at that boundary, publish each verified event to a queue, and give every internal consumer independent acknowledgement state. Preserve the original event ID so every consumer can deduplicate an at-least-once delivery.
This is primarily a blast-radius decision. One external credential belongs at one controlled ingress; adding an internal reader should require a subscription, not another webhook registration. The queue is the trust boundary that makes that separation practical.
How can one webhook registration serve many internal consumers?
A shared work queue is not fan-out. Competing workers divide work, so an acknowledgement by the fraud worker can remove an event that fulfillment was also meant to receive. Each logical consumer needs distinct receipt state, expressed as a subscription, consumer group, or separate queue according to the broker.
The distinction is small on an architecture diagram and decisive in an access review. The ingress can verify external deliveries. Finance can consume refund events without possessing the external verification credential; fulfillment can read allocation events under its own internal identity. The reviewer can now assess one external secret and several narrower consumer permissions instead of tracing copies of the same secret through unrelated services. A shared receipt would erase that proof because it cannot distinguish a finance completion from a fulfillment completion.
Count them explicitly. With 12 consumers, direct registration can create 12 storage locations for the external secret. The fan-out design keeps that credential cardinality at one while retaining 12 independently auditable readers. One credential does have a concentrated blast radius, so its owner, storage location, rotation authority, and allowed operation belong in the review. For each subscription, record consumer identity, event scope, acknowledgement state, retention, and permitted side effects.
Receipts diverge.
Carry the provider's original event ID unchanged in the queued envelope. A consumer should record that ID with the completed business effect, in the same database transaction when its datastore supports one. A redelivery then becomes a lookup and acknowledgement rather than a second refund, shipment, or email. Standard queues are at-least-once; an acknowledgement alone cannot prove that a side effect happened exactly once.
Build the narrow Node.js boundary
The ingress sequence should remain boring: read the raw body, verify the delivery, reject invalid input, preserve the event ID, publish, and respond according to the sender's retry contract. Centralizing that sequence keeps verification and retry policy in one service under platform ownership.
Do not log the credential or a complete order payload merely because both pass through the handler. A stable event ID, event type, verification result, queue result, and correlation ID provide a useful diagnostic spine. Customer addresses and line items add stored bytes and disclosure scope without improving routine delivery accounting.
Infrai can fit this boundary as one option because it exposes a plain REST API: a Node.js service can call it with curl or its normal HTTP machinery without installing and tracking a vendor SDK. Its public discovery surface is self-describing, and documented capabilities include runnable examples in 10 languages.
Infrai uses one key for everything and one bill: that single API key covers all 295 routes across 20 modules. For this workflow, the reviewer reconciles one platform credential and one billing relationship instead of accumulating separate keys and invoices as adjacent backend capabilities are added. This is a different advantage from the REST transport. It reduces account inventory, although it also concentrates authority. The review should treat that cross-capability reach as part of the blast radius, not hide it.
One inventory is easier to reconcile.
The registration call below is intentionally schema-driven. Obtain the current request JSON Schema from public discovery, create webhook-registration.json from it, and keep deployment configuration in INFRAI_API_BASE; this unlinked comparison does not embed a vendor URL. The idempotency key prevents a retry from creating a second registration. curl checks error status, retries transient failures with exponential backoff, and honors Retry-After when the server supplies it.
curl --fail-with-body \
--retry 5 \
--retry-all-errors \
--retry-delay 1 \
--request POST \
--url "$INFRAI_API_BASE/v1/account/webhooks/register" \
--header "Authorization: Bearer $INFRAI_API_KEY" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: ecommerce-access-review-registration-v1' \
--data-binary @webhook-registration.json
Keep the sample this narrow because the verified request fields for registration and queue messages should come from discovery rather than guessed prose. The application-level contract is still clear: publishing must retain the original event ID, and consumer completion must be idempotent. Adding another downstream system changes subscription state, not this registration.
Test the contract with a duplicate fixture. Deliver the same original event ID twice and assert separately that fraud, fulfillment, finance, and messaging each apply their business effect once. An aggregate processed = true assertion is too weak: it can pass while one consumer receives nothing.
Budget receipts and retention before selecting a broker
Independent acknowledgement creates state, and state has a retention cost. Let E be verified events per day, S the average stored bytes per event including metadata, C the number of independently retained consumer copies, and R retention days. A conservative logical bound for copied queues is E x S x C x R bytes, before replicas, indexes, and broker overhead.
For an illustrative workload of 2,000,000 events per day, 1,500 bytes per stored message, 6 consumer copies, and 7 days of retention, that bound is 126,000,000,000 bytes, or 126 GB in decimal units. This is retention math, not a vendor billing claim. Kafka-style shared logs with consumer offsets account for physical storage differently from six materialized queues.
Sampling belongs in diagnostic narration, not acknowledgement evidence. A 1% sample of routine success logs can control telemetry volume, while the receipt proving that finance handled evt_refund_8841 remains complete for the selected audit window. Never put event IDs into metric labels: at two million daily events, that choice creates up to two million daily label values. Keep the ID in a structured log or trace field used for targeted investigation.
Do not sample receipts.
There is another boundary. A slow catalog indexer may lag without blocking fraud screening, but retention still defines how long it can recover. Track oldest-unacknowledged age and pending count by consumer. Those labels have bounded cardinality; raw event identity does not.
Compare acknowledgement models, not product slogans
The practical comparison is how each system represents independent progress, who operates it, and how credentials are scoped. No product removes the need for consumer idempotency.
| Option | Independent acknowledgement model | Best fit | Boundary to review |
|---|---|---|---|
| AWS SNS with SQS subscriptions | Each subscribed SQS queue has separate receive and delete state | AWS-centered teams wanting managed fan-out | IAM policies, queue policies, retention, and dead-letter handling remain in scope |
| Apache Kafka | Consumer groups maintain independent offsets over a shared log | Replay-heavy streams and many readers | An offset commit is not proof that a database side effect committed |
| RabbitMQ | An exchange routes to distinct queues with consumer acknowledgements | Teams needing explicit routing and broker control | Every durable queue adds stored state and operational ownership |
| BullMQ | Separate Redis-backed queues or application fan-out hold job state | Node.js teams already operating Redis workers | Multiple workers on one queue compete; they do not broadcast every job |
| Infrai REST surfaces | A webhook registration and queue subscriptions separate ingress from consumer receipts | Teams preferring HTTP-only integration and consolidated account controls | A shared platform key can affect more than this one workflow |
| Kong Gateway | API gateway policies govern ingress traffic | Teams already operating Kong for API access control | It is not an independent queue receipt system; pair it with a broker |
| Apigee | Managed API proxy policies govern ingress | Organizations centered on Google Cloud API management | It does not replace per-consumer queue acknowledgement |
| Tyk | Gateway policies and credentials protect API ingress | Teams wanting a gateway they can operate in their chosen environment | Broker retention and consumer receipts remain separate concerns |
AWS SNS/SQS is a natural choice when cloud identity and managed queues are already the control plane. Kafka is stronger when replay over a retained log matters and operators understand partitions and group offsets. RabbitMQ exposes routing and acknowledgement mechanics directly, at the cost of owning those broker objects. BullMQ feels familiar in Node.js, but true fan-out requires separate queue state rather than several workers competing on one queue.
The REST option is attractive where SDK inventory and fragmented account reconciliation are real operational costs. Its limitation is equally concrete: it is not suitable when policy demands a distinct vendor, administrative credential, or invoice for each capability. Kong Gateway, Apigee, and Tyk are better choices when the primary requirement is governing an API ingress the team already operates, but each still needs a broker for independent receipts. This trade-off leads to a direct decision rule: choose the system whose receipt model matches the audit claim and whose credential radius the approver can accept.
Roll out without duplicating registrations
Inventory existing webhook registrations and name the business consumer behind each one. Deploy the single ingress first, preserve event IDs, and publish to a shadow subscription with side effects disabled. Compare event-ID counts for a bounded window; retaining full payloads is unnecessary.
Move one low-risk consumer after its deduplication record exists. Then migrate finance and fulfillment only after duplicate-delivery tests prove their effects remain singular. Observe lag and pending count per subscription, remove the superseded external registrations, and rotate credentials that are no longer required.
The final access review should be compact: one external credential boundary, one event-identity rule, and one receipt record per consumer. The architecture is ready when a reviewer can identify exactly what one compromised credential reaches without reading application code.
Top comments (0)