Rotating a Salla webhook endpoint safely is a state migration, not a DNS edit. Keep an authoritative inventory of each store's subscriptions, bring up the new receiver before changing anything, feed old and new deliveries into the same idempotent inbox, compare delivery health, remove only the old subscription by its ID, and run an API reconciliation before retiring the rollback path. This design helps Salla apps that move domains, regions, gateways or webhook infrastructure without silently losing order, product or customer events.
This is a practical engineering guide based on Salla's official Merchant API and partner documentation, verified on 7 October 2026. The current Register and Update Webhook pages were modified on 6 October 2026. Salla documents the endpoints, scopes, version behavior and removal semantics; the desired-state controller, cutover gates and recovery window below are engineering recommendations, not claims that Salla provides exactly-once delivery.
Treat subscriptions as managed state
The Register Webhook endpoint accepts an event, URL, name, version, optional rule and custom headers under the `webhooks.read_write` scope. Salla states that new subscriptions using the same URL update events or restore an old webhook if one exists. New registrations default to version 2 unless version 1 is requested explicitly. Those details make repeated blind POST requests unsafe as a migration plan: an apparent create may mutate or restore existing state instead of creating a second independent path.
Store one local subscription record per merchant and intended event. At minimum keep the Salla subscription ID, store ID, event, endpoint URL, payload version, conditional rule, secret reference, desired generation, observed generation, activation state and last verification time. Never persist a custom header secret in plaintext logs. The remote ID is operational data, not a disposable response field; it is the safest handle for a targeted update or removal.
Define desired state in code or a controlled configuration table. A controller can list remote state, compare it with the desired generation and propose changes. Keep discovery read-only with `webhooks.read`; grant `webhooks.read_write` only to the reconciler that applies reviewed changes. If hundreds of stores share an app, partition work per store and respect the same API admission budget used by the rest of the integration.
subscription_key = store_id + event + logical_consumer
desired = { url, version, rule, secret_ref, generation }
observed = { salla_id, url, version, rule, checked_at }
action = create | update | verify | retire | no_changeInventory before changing the route
Call List Active Webhooks for every installed store and persist a sanitized snapshot. The documented response includes ID, name, event, version, rule, type, URL and headers, plus pagination. Page through the entire response; a first page is not a complete inventory once a merchant has many subscriptions. Compare by remote ID first, then by event and normalized URL. Duplicate events may be intentional when two consumers serve different business workflows.
Classify each row as managed, foreign or ambiguous. Managed subscriptions are owned by the current app and match a local record. Foreign subscriptions may belong to another app or a merchant workflow and must not be modified. Ambiguous rows share a URL or name but lack enough evidence of ownership; stop and review rather than guessing. This matters because Salla's Deactivate endpoint can remove by URL, and that URL may be shared by several events.
The inventory is also the rollback manifest. Record the old ID, URL, version, rule and secret generation before cutover. Do not copy returned header values into tickets or traces. A controlled restore should be able to reconstruct the old receiver contract without exposing credentials.
Build one durable acceptance boundary
Deploy the new endpoint before registering it. It should validate the configured Salla token or signature on the unmodified request, reject untrusted traffic, insert a durable inbox row and acknowledge quickly. Salla's webhook guide describes token and signature strategies and instructs integrations to deny suspicious requests that cannot be authenticated. The receiver should do no ERP call, shipment booking or customer notification in the request path.
Both old and new receivers must write to the same logical inbox or to replicated inboxes with one shared idempotency store. A stable business key can combine the authenticated store, event, resource identity and source version or timestamp. When the provider payload exposes no suitable unique delivery identifier, keep a bounded payload fingerprint as a secondary duplicate signal and make every downstream side effect independently idempotent. A hash is not proof that two business events are identical, so retain the original event metadata needed for review.
const raw = await readRawBody(request);
const route = resolveReceiverGeneration(request.url);
verifySallaWebhook(raw, request.headers, route.secretRef);
const event = parseAfterVerification(raw);
const businessKey = deriveStableKey({
store: event.merchant,
type: event.event,
resource: sourceResourceId(event),
sourceVersion: sourceVersionOrTime(event)
});
await inbox.insertOrObserve({ businessKey, route, payload: encrypt(raw) });
return accepted();The exact key depends on the event contract. If an order can be updated several times with the same timestamp resolution, include an event-specific version or a guarded fingerprint and let the worker fetch current authoritative state before applying a transition. Do not promise exactly-once execution merely because the HTTP receiver has a unique constraint.
Use a six-gate cutover
First, deploy and probe the new endpoint with its own health, TLS and secret configuration. Second, register the new URL for a small canary set of stores and the same required event set. Use a distinct URL when you need parallel delivery: Salla documents that registering the same URL can update or restore existing subscriptions. Third, confirm the new rows through List Active Webhooks instead of trusting only the mutation response.
Fourth, observe both paths through the durable inbox. Compare authenticated deliveries, event mix, acknowledgement latency, invalid-signature rate, inbox lag and downstream success. Expect duplicates and prove they are harmless. Do not require perfect count equality over a tiny interval: event timing, retries and conditional rules can shift observations. Compare by store, event and a settled time window.
Fifth, stop the old subscription with the Deactivate Webhook endpoint using its exact ID. Salla also supports removal by URL and warns that using a URL deletes all registered webhooks for that URL. URL deletion is appropriate only when the inventory proves that every subscription on the endpoint is intentionally being retired. For ordinary rotation, ID-targeted removal narrows the blast radius. The documented success response is HTTP 202, so follow the request with another active-list read and treat that observed absence as the postcondition.
Sixth, keep the old receiver alive but non-authoritative for a bounded cooldown. It should still authenticate and record late requests without triggering duplicate effects. Run reconciliation against the relevant Salla resource APIs from a checkpoint before cutover through a safe horizon after it. Only then remove the old route, secret and infrastructure.
Separate endpoint, secret and payload-version rotation
Changing the URL, webhook authentication secret and payload version at the same instant creates three possible failure sources. Prefer independent generations. Move traffic to the new endpoint while accepting the current and next secret references for a short controlled window. After both paths are healthy, rotate the secret. Upgrade payload version only after parsers and fixtures accept both schemas.
The Update Webhook endpoint updates an existing subscription by ID and supports name, version, rule and headers. Use it for an in-place contract change when parallel delivery is unnecessary and rollback is straightforward. Use a new URL and overlapping receivers when infrastructure, region or security boundaries change and you need observable cutover evidence.
Keep secret generations in a managed secret store. The subscription table should hold a reference and fingerprint, never the secret value. Verification can accept two generations briefly, but outbound configuration must have one declared active generation. Remove the old secret after the cooldown and prove that no receiver process still loads it.
Make rollback explicit
Rollback is not “point DNS back.” It is a controlled transition with a known subscription ID and receiver generation. Trigger rollback if the new endpoint fails authentication, durable acceptance, latency, event-mix or reconciliation gates. Re-enable or re-register the old desired state from the manifest, verify it through List Active Webhooks, and leave the new path collecting evidence without applying effects until the incident is understood.
If the old subscription was removed by URL and several events disappeared, restore from the full inventory rather than guessing from the latest alerts. If the mutation result is uncertain because of a timeout, list active subscriptions before retrying; the same-URL restore/update behavior can otherwise hide which remote state exists. Every control-plane mutation should be followed by an observed-state read.
Keep an audit record with actor or job identity, store, old and new remote IDs, previous and desired contracts, mutation response, observed postcondition and timestamps. Redact tokens, signatures, custom headers and customer payloads. This record helps distinguish a provider delivery gap from an application configuration mistake.
Offboard the installation, not only the URL
Salla's App Events documentation includes `app.uninstalled` among lifecycle events. Treat uninstall as a high-priority state transition: mark the installation revoked, stop new jobs, invalidate cached credentials, prevent refresh attempts and begin retention-aware deletion. A queue item created before uninstall must recheck installation state before calling Salla or applying a merchant-side effect.
Do not depend on a final successful Merchant API cleanup after uninstall; credentials may no longer be usable. Maintain enough local ownership data to disable processing without a remote call. Webhook subscription inventory is still useful for audit, but local authorization state is the immediate safety boundary. Separate uninstall from temporary delivery failure so an outage does not accidentally trigger data deletion.
A domain migration is not an uninstall. Keep the merchant installation identity stable while receiver generations change. Do not create a second tenant record merely because the callback hostname changed. The installation owns subscriptions; the endpoint is versioned infrastructure beneath it.
Best practices and anti-patterns
Use a desired-state controller when many stores share the same event contract, when endpoint migration crosses regions or gateways, or when auditability and rollback matter. Use an in-place update for a low-risk metadata or rule change only when you can prove the current ID, accept a short non-overlap and have a tested reversal. Use a reverse proxy or stable ingress hostname when infrastructure changes frequently; rotating an internal upstream behind that boundary may avoid a remote subscription change altogether.
Avoid blind create loops, URL-wide deletion without an ownership proof, secret values in logs, side effects in the receiver, count-only comparisons, and disabling the old route immediately after a 202 response. Do not let an AI agent call generic webhook mutation tools with merchant tokens. Expose narrow operations such as `planRotation`, `registerCanary`, `verifyInventory` and `retireById`, and keep approval around destructive steps.
The practical rule is simple: inventory before mutation, create overlap with a distinct URL, authenticate before parsing, store before acknowledging, deduplicate at the business boundary, verify remote postconditions and reconcile after cutover. For adjacent designs, see the Salla and Zid webhook recovery guide, Salla conditional webhooks and backend API integration services.
Official references
These references document the tools discussed. Examples and design decisions are illustrative and should be adapted to the project and its versions.
Prepared by: Noor Yasser
Working through a similar engineering challenge?
I help teams turn architecture decisions into a clear scope and dependable, reviewable implementation.




